动态规划(1)——0-1背包问题
1 题目描述
对于一组不同重量、不可分割的物品,我们需要选择一些装入背包,在满足背包最大重量限制的前提下,背包中物品总重量的最大值是多少呢?
2 输入
第一行是物品的个数n(1≤n≤100000),背包容量w(1≤w≤1000000);
第二行是n个物品的重量。
3 输出
输出最大值
4 样例输入
5 9
2 2 4 6 3
5 样例输出
9
6 求解思路
把问题分解为多个阶段,每个阶段对应一个决策。我们记录每一个阶段可达的状态集合(去掉重复的),然后通过当前阶段的状态集合,来推导下一个阶段的状态集合,动态地往前推进。这也是动态规划这个名字的由来,你可以自己体会一下,是不是还挺形象的?
7 C++版本代码如下
#include <iostream>
#include <math.h>
#include <string.h>
using namespace std;
#define MAXNUM 100010
int dpFirst(int weight[], int n, int weightLimit){
bool states[n][weightLimit + 1];
memset(states, false, sizeof(states));
// 第一行单独处理
states[0][0] = true;
if(weight[0] <= weightLimit)
states[0][weight[0]] = true;
// 动态规划状态转移
for(int i = 1; i < n; i++){
for(int j = 0; j < weightLimit + 1; j++)
if(states[i - 1][j]){
// 不装
states[i][j + 0] = true;
// 装
states[i][j + weight[i]] = true;
}
}
// 找出最后一个状态下的背包值
int maxPower = 0;
for(int i = 0; i <= weightLimit; i++)
if(states[n - 1][i])
maxPower = i;
return maxPower;
}
int main()
{
int weight[5] = {2,2,4,6,3};
cout<<dpFirst(weight, 5, 9)<<endl;;
return 0;
}
因为只需要判断最后一个状态下的背包容量,所以可以使用一个数组states[weightLimit]即可解决问题,代码可优化空间复杂度如下:
其中注意上一个二维数组的方法中数组weight的访问可能越界,所以修改为j <= weightLimit - weight[i + 1]
保证了数组weight不会越界,表示“当前状态”能装下的。
int dpFirst(int weight[], int n, int weightLimit){
bool states[weightLimit + 1];
memset(states, false, sizeof(states));
// 单独处理第一个状态
states[0] = true;
if(weight[0] <= weightLimit)
states[weight[0]] = true;
// 动态规划状态转移用一个数组解决
for(int i = 1; i < n; i++){
// 可以直接写成weightLimit - weight[i],而不是weightLimit
// 这样可以少几个判断,并且保证了数组weight不会越界
for(int j = 0; j <= weightLimit - weight[i]; j++){
if(states[j])
// 装
states[j+ weight[i]] = true;
}
}
// 找出最后一个状态下的背包值
for(int i = weightLimit; i >= 0; i--)
if(states[i])
return i;
}