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。所以枚举时有这样的改进:
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刚好用完的。(看看代码就能明白)
Piggy-Bank(多重背包取最小)
http://acm.hdu.edu.cn/showproblem.php?pid=1114
#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;
}
悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(简单多重背包)
http://acm.hdu.edu.cn/showproblem.php?pid=2191
#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;
}
Big Event in HDU (wei=val 多重背包)
http://acm.hdu.edu.cn/showproblem.php?pid=1171
#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;
}
最大报销额(wei=val浮点01背包)
http://acm.hdu.edu.cn/showproblem.php?pid=1864
#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;
}
湫湫系列故事——减肥记I (简单完全背包)
http://acm.hdu.edu.cn/showproblem.php?pid=4508
#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;
}