01背包:
有n 种不同的物品,每一种物品一件,每个物品有两个属性,wei重量(体积),val 价值,现在给一个容量为 V 的背包,问最多可带走多少价值的物品。
完全背包:
在01背包的基础上,如果物品不计件数,就是每个物品无限件的话,求出结果。
多重背包:
在01背包的基础上,每一件物品的件数是一定的(给出的),求出结果。
01背包:
状态转移方程(默认求最大价值):dp[j]=dp[j-wei[i]]+val[i]>dp[j]?dp[j-wei[i]]+val[i]:dp[j]
完全背包:for (int i=0; i<n; i++){ for (int j=V; j>=wei[i]; j--){ int t = dp[j-wei[i]]+val[i]; dp[j] =dp[j]>t?dp[j]:t; } }
为了累加效果,所以从前向后遍历:多重背包:for (int i=0; i<n; i++){ for (int j=wei[i]; j<=V; j++){ int t = dp[j-wei[i]]+val[i]; dp[j] =dp[j]>t?dp[j]:t; } }
把每一中物品的个数用二进制原理展开,转成01背包处理:
多重背包的二进制转化原理:把一个数字展开成二进制,我们可以发现一个数字可以由小于他的1,10,10,1……组成。有一个误区,例子:10转化成二进制后:1010,设c=10, 如果直接用for(int i=1; i<=c; i<<=1) 枚举我们将得到1, 10, 100, 1000 这几个二进制数,存在结果大于10的组合(如1111)。想要将多重背包转化成01背包,就必须保证得到的数字的任意组合小于等于10。所以枚举时有这样的改进:f or(int i=1;i<=c;i<<=1){//---c-=i;}if(c>0){ //-- }得到的数字是1,10,100,11。前三个数字的组合结果包含了0——7,最后一个数字是3,那么结果就是0——10。刚好满足条件。
另外,如果DP的初始化是0,那么得出结果是V可以不用完的,也能是用完的。当初始化为负无穷小 -inf,dp[0]=0时,结果是V刚好用完的。(看看代码就能明白)
hdu 1114 Piggy-Bank(多重背包取最小)#include <iostream> #include <cstring> #include <cstdio> using namespace std; typedef long long LL; LL n,V,wei[505],val[505],dp[10005]; LL work(){ for(int i=0;i<n;i++){ for(int j=wei[i];j<=V;j++){ int t=dp[j-wei[i]]+val[i]; dp[j]=t<dp[j]?t:dp[j]; } } return dp[V]; } int main() { //freopen("cin.txt","r",stdin); int t; cin>>t; while(t--){ LL E,F; scanf("%lld%lld",&E,&F); V=F-E; memset(dp,0x3f,sizeof(dp)); dp[0]=0; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d%d",&val[i],&wei[i]); } LL ans=work(); if(ans>=0x3f3f3f3f) printf("This is impossible.\n"); else printf("The minimum amount of money in the piggy-bank is %lld.\n",ans); } return 0; }
hdu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(简单多重背包)#include <iostream> #include <cstdio> #include <cstring> using namespace std; int wei[505],val[505],dp[105]; int n,V; int main() { int t,m; cin>>t; while(t--){ scanf("%d%d",&V,&m); int w,v,c; n=0; for(int i=0;i<m;i++){ scanf("%d%d%d",&w,&v,&c); for(int j=1;j<=c;j<<=1){ wei[n]=w*j; val[n++]=v*j; c-=j; } if(c>0) { wei[n]=w*c; val[n++]=v*c; } } memset(dp,0,sizeof(dp)); int ans=0; for(int i=0;i<n;i++){ for(int j=V;j>=wei[i];j--){ int t=dp[j-wei[i]]+val[i]; dp[j]=dp[j]>t?dp[j]:t; ans=ans>dp[j]?ans:dp[j]; } } printf("%d\n",ans); } return 0; }
hdu 1171 Big Event in HDU (wei=val 多重背包)#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N=3e5+10; int wei[400],dp[N],cnt; int main() { //freopen("cin.txt","r",stdin); int n; while(cin>>n&&(n>0)){ cnt=0; int w,c; int V=0; for(int i=0;i<n;i++){ scanf("%d%d",&w,&c); V=V+w*c; for(int j=1;j<=c;j<<=1){ wei[cnt++]=w*j; c-=j; } if(c>0) wei[cnt++]=w*c; } int V2=V/2; memset(dp,0,sizeof(dp)); for(int i=0;i<cnt;i++){ for(int j=V2;j>=wei[i];j--){ int t=dp[j-wei[i]]+wei[i]; //price同时是重量和价值 dp[j]=t>dp[j]?t:dp[j]; } } int ans2=dp[V2],ans1=V-ans2; printf("%d %d\n",ans1,ans2); } return 0; }
hdu 1864 最大报销额(wei=val浮点01背包)#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N=3e6+10; int wei[N],dp[N]; bool check(char ch){ if(ch=='A'||ch=='B'||ch=='C') return true; return false; } int main() { //freopen("cin.txt","r",stdin); double q; int n,m; while(cin>>q>>n&&n){ int cnt=0; for(int i=0;i<n;i++){ scanf("%d",&m); char s1,s2; double p; int sum=0,aa=0,bb=0,cc=0; bool tag=1; //attention: 单项(类)物品的价值不得超过600元. for(int j=0;j<m;j++){ scanf(" %c%c%lf",&s1,&s2,&p); int x=int(p*100); sum+=x; if(check(s1)==false) tag=0; if(tag){ if(s1=='A') aa+=x; else if(s1=='B') bb+=x; else cc+=x; } } if(aa<=60000&&bb<=60000&&cc<=60000&&sum<=100000&&tag) wei[cnt++]=sum; } memset(dp,0,sizeof(dp)); int qq=int(q*100); for(int i=0;i<cnt;i++){ for(int j=qq;j>=wei[i];j--){ int t=dp[j-wei[i]]+wei[i]; //价格既有重量的性质又有价值的性质 dp[j]=t>dp[j]?t:dp[j]; } } printf("%.2lf\n",dp[qq]/100.0); } return 0; }
hdu 4508 湫湫系列故事——减肥记I (简单完全背包)#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long LL; const LL N=105,M=1e5+10; LL n,V; LL wei[N],val[N],dp[M]; LL work(){ memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++){ for(int j=wei[i];j<=V;j++){ LL t=dp[j-wei[i]]+val[i]; dp[j]=dp[j]>t?dp[j]:t; } } return dp[V]; } int main() { //freopen("cin.txt","r",stdin); while(cin>>n){ for(int i=0;i<n;i++){ scanf("%I64d%I64d",&val[i],&wei[i]); } scanf("%I64d",&V); memset(dp,0,sizeof(dp)); printf("%I64d\n",work()); } return 0; }