背包问题
2016-09-25 11:10
169 查看
物品无限的背包问题:
题意:有n个物品,每件均有无穷多个,第i个物品的体积为V[i],重量为W[i]。选一些物品装到容量为C的背包中,使得物品的总体积不大于背包的体积情况下重量最大。
分析:
这个问题和之前的硬币问题类似,增加了一个新的属性——重量,把无权图变成了有权图,很简单就把”+1“改成“+W[i]”就行了;
代码:
0-1背包问题
题意:有n个物品,第i个物品的体积为V[i],价值为W[i],容积为C的背包。求在物品总体积不超过其容量下,得到的价值最大。
分析:
这个问题用刚才的方法已经不适用了,只凭“剩余体积”这个状态,无法得之每个物品是否用过。刚才的状态很混乱,相当于是每次都对每个物品进行寻找一次。应该抓住每个物品只有一次的条件,所以dp[i][j]表示 从0到i个物品中,体积为j的背包所得到的最大价值。也就是说,对于每一个物品都进行一次询问,是否放进不同容量的背包,放进去与不放进去的进行比较。选择更大价值的一个。因而其状态转移方程就已经出来了。如下:
其具体代码如下:
其中的可以不需要保存V[i]和W[i]的,可以直接输入直接进行运算:
更奇妙的是可以把它变成维数组:
对于变成一维数组就感觉很神奇,但是自己不是很能理解为什么可以,就先留着疑问吧,以后在接触到其他的加以理解。
题意:有n个物品,每件均有无穷多个,第i个物品的体积为V[i],重量为W[i]。选一些物品装到容量为C的背包中,使得物品的总体积不大于背包的体积情况下重量最大。
分析:
这个问题和之前的硬币问题类似,增加了一个新的属性——重量,把无权图变成了有权图,很简单就把”+1“改成“+W[i]”就行了;
代码:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int maxx=10000; const int INF=(1<<30); int n,C,V[maxx],W[maxx],dp[maxx]; int DP(int c) { int &cns=dp[c]; if(cns!=-1) return cns; cns=-INF; for(int i=0;i<n;i++) if(c>=V[i]) cns=max(cns,DP(c-V[i])+W[i]); return cns; } int main() { cin>>n>>C; for(int i=0;i<n;i++) cin>>V[i]>>W[i]; memset(dp,-1,sizeof(dp)); cout<<"最大重量为:"<<DP(C)<<endl; return 0; }
0-1背包问题
题意:有n个物品,第i个物品的体积为V[i],价值为W[i],容积为C的背包。求在物品总体积不超过其容量下,得到的价值最大。
分析:
这个问题用刚才的方法已经不适用了,只凭“剩余体积”这个状态,无法得之每个物品是否用过。刚才的状态很混乱,相当于是每次都对每个物品进行寻找一次。应该抓住每个物品只有一次的条件,所以dp[i][j]表示 从0到i个物品中,体积为j的背包所得到的最大价值。也就是说,对于每一个物品都进行一次询问,是否放进不同容量的背包,放进去与不放进去的进行比较。选择更大价值的一个。因而其状态转移方程就已经出来了。如下:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i]);
其具体代码如下:
/* 在状态转移方程上面出错了,之前的状态是dp[i][j]=max(dp[i-1][j],dp[i][j-V{i]]+W[i]) 之所以会这么写,是因为我感觉dp[i][j]就是表示在0到i个物品中,体积为j所得到的最大价值 所以dp[i][j-V[i]]就是表示把第i个物品放入其中,但是很显然dp[i][j-V[i]]没有计算出来,这样 得到结果不对,应该是对于第i-1个物品才有意义,dp[i-1][j-V[i]]就是表示把第i个物品放 入在从0到i个物品中的值,最后才把该值赋值给dp[i][j]。所以正确的状态转移方程是: dp[][]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i]) */ #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> using namespace std; const int maxx=10000; int n,C,V[maxx],W[maxx],dp[maxx][maxx]; int main() { cin>>C>>n; for(int i=1;i<=n;i++) cin>>V[i]>>W[i]; memset(dp,0,sizeof(dp)) for(int i=1;i<=n;i++) for(int j=0;j<=C;j++) { if(i==1) dp[i][j]=0; if(j>=V[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i]); } cout<<dp [C]<<endl; return 0; }
其中的可以不需要保存V[i]和W[i]的,可以直接输入直接进行运算:
for(int i=1;i<=n;i++) { cin>>V>>W for(int j=0;j<=C;j++) { if(i==1) dp[i][j]=0; if(j>=V[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-V]+W); } }
更奇妙的是可以把它变成维数组:
for(int i=1;i<=n;i++) { cin>>V>>W; for(int j=C;j>=0;j--) { if(i==1) dp[i][j]=0; if(j>=V[i]) dp[j]=max(dp[j],dp[j-V]+W); } }
对于变成一维数组就感觉很神奇,但是自己不是很能理解为什么可以,就先留着疑问吧,以后在接触到其他的加以理解。