完全背包

来源:互联网 发布:女鞋原单淘宝推荐 编辑:程序博客网 时间:2024/06/02 23:53

有n个物品,每个物品体积是costs = {c1,c2,...cn},每个物品的价值是values = {v1,v2,...vn},每个物品只能取无限次。现在有体积为v的背包,问将这些物品放入该背包,能得到的最大价值是多少?并输出最大时的选择方案。

推导:设dp[i][j]为将前i个物品放入体积为j的背包时取得的最大价值。来看,dp[i][j]可由哪些状态得到?

1、若第i个物品不放入背包,那么dp[i][j] = dp[i - 1][j];

2、若第i个物品放入背包,那么此时dp[i][j]不能再像01背包那样子等于dp[i - 1][j - costs[i]] + values[i]了,因为dp[i - 1][k]前i - 1个物品,可现在的要求是每个物品都可以放无数次。那么,就应该是dp[i][j] = dp[i][j - costs[i]] + values[i],表示dp[i][j]是由当前i状态得来的而不是由它的前一个状态i - 1得来的。

那么完全背包的状态方程就应该是:dp[i][j] = max(dp[i - 1][j],dp[i][j - costs[i]] + values[i])(i = 0 或者j = 0时dp[i][j] = 0),由此可写出代码:

for(int i = 1;i <= n;i++)for(int j = 0;j <= v;v++)if(j < costs[i])dp[i][j] = dp[i - 1][j];elsedp[i][j] = max(dp[i - 1][j],dp[i][j - costs[i]] + values[i]);
由此可看出完全背包的时间复杂度为O(nv),空间复杂度为O(nv)。同01背包一样,我们同样可以用滚动数组将其空间复杂度降为O(v)。

for(int i = 1;i <= n;i++)for(int j = costs[i];j <= v;j++)dp[j] = max(dp[j],dp[j - costs[i]] + values[i]);
可以看到,区01背包的区别只是内层循环变为顺序了。关于利用滚动数组来降低空间复杂度,只要在纸上画下就清楚了。
我们再来看如何记录路径。我们用path[i][j] = (x,y)表示从(x,y)到(i,j)有条路径,那么i != x的时候说明第i个物品没有放进背包。

#define N 1001struct CompletePack{/* *numOfres:物品的个数 *volumeOfpack:背包容量 *path:记录路径 *costs[i]:第i个物品的消耗 *values[i]:第i个物品的价值 *求放进容量为volumeOfpack的背包能取得的最大价值 */int dp[N];int path[N][N][2];int costs[N],values[N],numOfres,volumeOfpack;CompletePack(int numOfres,int volumeOfpack,int costs[],int values[]){this->numOfres = numOfres;this->volumeOfpack = volumeOfpack;for(int i = 1;i <= this->numOfres;i++)this->costs[i] = costs[i],this->values[i] = values[i];memset(dp,0,sizeof(dp));memset(path,0,sizeof(path));}int getMaxValue(){for(int i = 1;i <= numOfres;i++){int j;for(j = 0;j < costs[i];j++)path[i][j][0] = i - 1,path[i][j][1] = j;for(;j <= volumeOfpack;j++){int tmp = dp[j - costs[i]] + values[i];if(dp[j] < tmp){dp[j] = tmp;path[i][j][0] = i,path[i][j][1] = j - costs[i];}elsepath[i][j][0] = i - 1,path[i][j][1] = j;}}return dp[volumeOfpack];}/* * 输出路径,path[i][j] = (x,y),表示从(x,y)到(i,j)有一条路径,当path[i][j] = (i - 1,j)时表示第i个物品没有放入背包 * 初始参数为(numOfres,volumeOfpack) */void getPath(int i,int j){if(!i || !j)return;if(path[i][j][0] == i - 1){getPath(i - 1,j);return;}int num = 0;//记录第i个物品放入背包的个数while(path[i][j][0] == i){num++;j = path[i][j][1];}getPath(i,j);//values[i]*numprintf("values[%d]:%d*%d  ",i,values[i],num);}};

题目:

1、hdu 1248 模板题

2、hdu 1114 求刚好能放满v的背包时的最小价值。初始化时dp[0] = 0,其它为INF,表示不合法。

3、hdu 1284 完全背包求排列组合的总方案数。设dp[i][j]为前i个物品能组成容量j的方案数,那么dp[i][j] = dp[i - 1][j] + dp[i][j - costs[i]]。

4、poj 3181 同1284一样,也是求方案数。不过这题结果超过了long long,要用到大数相加。

5、hdu 2159 二维完全背包。用dp[i][j]表示用i的忍耐值和杀j只怪能得到的最大经验值。因为题目要求求能升级的消耗最少的忍耐值的方案,故最后遍历dp数组找个最优值就OK了。

原创粉丝点击