hdu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)
题意:给出经费的最大值n,再给出 种类m和每种的袋数c、价格p、重量h,求能买大米的最大重量
思路:每种物品有一个固定的次数上限。为多重背包问题。转换为01背包来做
以下方法,均为转化为01背包来做
思路1:物品不摊开,选取每一种时,进行讨论,(相当于竖着填背包v的一列,一列一列的填
码1:kj
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; int dp[110]; int main() { int i,j,k,tem; int t,n,_v;//测试用例个数,物品种类,背包大小 int c[110],v[110];//花费,价值 int num[110];//每种物品个数 int tc,tv;//拆分时物品花费,价值 scanf("%d",&t); while(t--) { scanf("%d%d",&_v,&n); memset(dp,0,sizeof(dp)); for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]); ////// for(i=1; i<=n; ++i) for(k=_v; k>=c[i]; --k) for(j=1; j<=num[i]&&j*c[i]<=k; ++j)//此处比01背包多了一层循环 { tc=j*c[i]; tv=j*v[i]; tem=dp[k-tc]+tv; if(tem>dp[k])dp[k]=tem; } // printf("%d\n",dp[_v]); } return 0; }
思路2:物品摊开,然后处理01背包(相当于横着填物品的一行,一行一行的填
2.1朴素拆分,把每种物品展开,既有n件a物品,则进行n件是否选取操作
码2:jk
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; int dp[110]; int main() { int i,j,k,tem; int t,n,_v;//测试用例个数,物品种类,背包大小 int c[110],v[110];//花费,价值 int num[110];//每种物品个数 int tc,tv;//拆分时物品花费,价值 scanf("%d",&t); while(t--) { scanf("%d%d",&_v,&n); memset(dp,0,sizeof(dp)); for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]); ////// for(i=1; i<=n; ++i) for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)//此处比01背包多了一层循环 for(k=_v; k>=j*c[i]; --k) { tem=dp[k-c[i]]+v[i]; if(tem>dp[k])dp[k]=tem; } // printf("%d\n",dp[_v]); } return 0; }
hint:2.1代码与思路1 代码 只是循环次序不一样而已,实际是一样的操作。(并且时间复杂度相同,略麻烦。
因为
思路1:
for(k=_v; k>=c[i]; --k)
for(j=1; j<=num[i]&&j*c[i]<=k; ++j)
思路2:
for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)
for(k=_v; k>=j*c[i]; --k)
大体看一下这俩循环,
这两个循环做的循环次数是相同的,也就是说,计算次数相同,时间复杂度相同
2.2 二进制拆分,有n件a物品,则拆成 1,2,4,8,...,q 这样,即 1+2+4+...+q=n
码3:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; int dp[110]; int main() { int i,j,k,tem; int t,n,_v;//测试用例个数,物品种类,背包大小 int c[110],v[110];//花费,价值 int num[110];//每种物品个数 int tc,tv;//拆分时物品花费,价值 scanf("%d",&t); while(t--) { scanf("%d%d",&_v,&n); memset(dp,0,sizeof(dp)); for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]); ////// for(i=1; i<=n; ++i) { for(j=1; j<=num[i]; num[i]=num[i]-j,j=j*2)//此处比01背包多了一层循环 { tc=j*c[i];//拆分后物品花费 tv=j*v[i];// for(k=_v; k>=tc; --k) { tem=dp[k-tc]+tv; if(tem>dp[k])dp[k]=tem; } } if(num[i]>0) //如果还有物品,num[i] 即相当于 1+2+4+...+q 中的 q { tc=num[i]*c[i]; tv=num[i]*v[i]; for(k=_v; k>=tc; --k) { tem=dp[k-tc]+tv; if(tem>dp[k])dp[k]=tem; } } } // printf("%d\n",dp[_v]); } return 0; }