您的位置:首页 > 其它

完全背包--动态规划

2015-07-08 09:45 134 查看
题目:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。放入第i种物品的费用是Ci,价值是Wi,求解:将哪些物品装入背包,可使这些物品耗费的费用和不超过背包容量,且价值总和最大。
分析:
(一)建立状态方程
可以转化为01背包问题求解
dp[i][v]表示前i件种物品放入容量为v的背包的最大价值,则有:
dp[i][v]=max{dp[i-1][v-k*C[i]]+k*W[i]},其中0<=k*C[i]<=v,状态方程的建立不难理解
(二)根据状态方程实现代码
有了状态方程,看出i与i-1有关,所以i从1到N遍历,v与v-k*C[i]有关,所以k从0到k*C[i]<=v遍历

[cpp] view plaincopy





#include <iostream>

#include <algorithm>

using namespace std;

#define N 3//物品个数

#define V 10//背包容量

int C[N+1]={0,10,4,5}; //Ci表示第i件物品的费用 (i从1开始)

int W[N+1]={0,2,2,1}; //W[i]表示第i件物品的价值

int dp[N+1][V+1];

int CompletePack(){

for(int i=1;i<=N;i++){

for(int k=0;k*C[i]<=V;k++)

dp[i][V]=max(dp[i][V],dp[i-1][V-k*C[i]]+k*W[i]);

}

return dp
[V];

}

int main()

{

memset(dp,0,sizeof(dp));

cout<<CompletePack();

return 0;

}

(三)优化数据结构
注意到状态方程中i只与i-1有关,可以消去一维,但是如果直接由上面的代码消维是不成的,因为我们用到了上一状态的V-k*C[i],也就是我们在进入下一状态前,这一状态的每一个dp[v],0<=v<=V,必须都是已知的,但是以上代码是对k进行遍历,和v无关,所以要对v进行遍历,那该如何写呢?
从状态方程中找线索,dp[i][v]=max{dp[i-1][v-k*C[i]]+k*W[i]},0<=k*C[i]<=V
我们再把它展开一下看,dp[i][v]=max{dp[i-1][v],dp[i-1][v-C[i]]+W[i],dp[i-1][v-2C[i]]+2W[i]……}
再来看我们需要的v,这个v范围是0<=v<=V,将v的范围带入上式看,我们发现虽然v-C[i],v-2C[i]看起来很繁琐,但是追根究底范围也一定是在0到V之间,
再来看它们之间的关系,发现后一个元素永远比前一个元素多了一个W[i],如果我们事先将dp[v-C[i]]+W[i]的值得到,那么当v增加一个C[i]时,取dp[v-C[i]],这里dp[v-C[i]]就是dp[v-C[i]]+W[i],然后再加上一个W[i],即dp[v-C[i]]+W[i]不就是取得两件i物品所获得的价值了吗!想到这,代码就可以实现了:

[cpp] view plaincopy





#include <iostream>

#include <algorithm>

using namespace std;

#define N 3//物品个数

#define V 10//背包容量

int C[N+1]={0,10,4,5}; //Ci表示第i件物品的费用 (i从1开始)

int W[N+1]={0,2,2,1}; //W[i]表示第i件物品的价值

int dp[V+1];

int CompletePack(){

for(int i=1;i<=N;i++){

for(int v=C[i];v<=V;v++)

dp[v]=max(dp[v],dp[v-C[i]]+W[i]);

}

return dp[V];

}

int main()

{

memset(dp,0,sizeof(dp));

cout<<CompletePack();

return 0;

}

发现了吗?代码和01背包问题就内层循环的顺序不同而已,但是实际意义可是大不相同啊

(四)深入优化
如果再继续优化也是可以的,如果有两件物品i和j,有C[i]>C[j],W[i]<W[j],也就是i物品所占容量大于j物品,但是i物品的价值却低于j物品,那么i物品就可以不用考虑了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: