1.混合了贪心思想的背包入门
nefu1028暑假计划 01背包
给定工作开始时间、完成时间、给的工资,工作不能重叠,求最大收益。
一维Dp表示截止到当前时间的最大收益,但是事先要对结构体按结束时间排序,防止前一状态没有值
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; int dp[1005]; struct money { int l,r,w; }a[1005]; int cmp(money x,money y)//排序都快忘了怎么写了= = { if(x.r==y.r) return x.l<y.l; return x.r<y.r; } int main() { // freopen("cin.txt","r",stdin); int n,t,m; scanf("%d",&t); while(t--) { memset(dp,0,sizeof(dp)); scanf("%d%d",&m,&n);//假期时间m和可做的工作数n for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].w); sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) { for(int j=m;j>=a[i].r;j--) { dp[j]=max(dp[j],dp[j]-(dp[a[i].r]-dp[a[i].l-1])+a[i].w);//重点的重点 } } printf("%d\n",dp[m]); } }
2.凑一半 裸的背包
UVA 562 Dividing coins 01背包
#include <iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> using namespace std; int price[50500],f[50500],n,t,sum; int dp() { memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { for(int j=sum;j>=price[i];j--) { if(f[j]<f[j-price[i]]+price[i]) f[j]=f[j-price[i]]+price[i]; } } return f[sum]; } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d",&t)) { while(t--) { scanf("%d",&n); sum=0; for(int i=1;i<=n;i++) { scanf("%d",&price[i]); sum+=price[i]; } int cnt=sum; sum=(sum+1)/2; cnt=dp()*2-cnt; if(cnt<0) cnt=-cnt; printf("%d\n",cnt); } } return 0; }
3.概率不超过某值的最大收益
hdu2955 Robberies 01背包的变形
加法变成乘法而已
#include <iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; double pj[1050],dp[100500],p,rate; int t,m[1050],n,sum; double max(double a,double b) { if(a<b) return b; else return a; } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d",&t)) { while(t--) { memset(dp,0,sizeof(dp)); dp[0]=1; sum=0; scanf("%lf%d",&p,&n); p=1-p; for(int i=0;i<n;i++) { scanf("%d%lf",&m[i],&pj[i]); pj[i]=1-pj[i]; sum+=m[i]; } for(int i=0;i<n;i++) { for(int j=sum;j>=m[i];j--) dp[j]=max(dp[j],dp[j-m[i]]*pj[i]); } for(int i=sum;i>=0;i--) { if(dp[i]>p) { printf("%d\n",i); break; } } } } return 0; }
4."混合背包"
2015多校联合第十场hdu5410CRB and His Birthday 01背包+完全背包
完全背包中若选择某种物品选一次加一个a[i],另加b[i]的价值,只加一次。在第二层for循环中先进行一次01背包算b[i]+a[i],再进行一次多重背包算a[i]
#include <iostream> #include<cstring> #include<cstdio> using namespace std; int dp[2005],cost[2005],a[2005],b[2005]; int main() { //freopen("cin.txt","r",stdin); int t,m,n; while(cin>>t) { while(t--) { cin>>m>>n; memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) cin>>cost[i]>>a[i]>>b[i]; for(int i=0;i<n;i++) { for(int j=m;j>=cost[i];j--) if(dp[j]<dp[j-cost[i]]+a[i]+b[i]) dp[j]=dp[j-cost[i]]+a[i]+b[i]; for(int j=cost[i];j<=m;j++) if(dp[j]<dp[j-cost[i]]+a[i]) dp[j]=dp[j-cost[i]]+a[i]; } cout<<dp[m]<<endl; } } return 0; }
5.01背包入门题
0-1背包:nefu19采药hdu2546饭卡
#include <iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; int n; int price[1005],f[1005]; int m; int main() { //freopen("data.in.txt","r",stdin); while(~scanf("%d",&n)&&n) { for(int i=1;i<=n;i++) scanf("%d",&price[i]); scanf("%d",&m); f[0]=0; //for(int i=1;i<=n;i++) f[i]=-999999; memset(f,0,sizeof(f)); sort(price+1,price+n+1); for(int i=1;i<=n-1;i++) { for(int v=m-5;v>=price[i];v--) f[v]=max(f[v],f[v-price[i]]+price[i]); } if(m>=5) printf("%d\n",m-f[m-5]-price[n]); else printf("%d\n",m); } return 0; }
#include <iostream> #include<cstdio> #include<cstring> //#define INF 0x7ffffff using namespace std; int cost[1005]; int price[1005]; int f[1005]; int main() { int m,t; while(~scanf("%d%d",&t,&m)) { f[0]=0; memset(f,0,sizeof(f)); //for(int i=1;i<=m;i++) f[i]=-999999; for(int i=1;i<=m;i++) scanf("%d%d",&cost[i],&price[i]); for(int i=1;i<=m;i++) { for(int v=t;v>=cost[i];v--) f[v]=max(f[v],f[v-cost[i]]+price[i]); } printf("%d\n",f[t]); } return 0; }
6.同第二题 凑一半
hdu1171big events in hdu【多重背包模板】
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[50005]; int value[60],num[60]; int n,total,sum; void zero(int cost,int weight) { for(int i=total;i>=cost;i--) dp[i]=max(dp[i],dp[i-cost]+weight); } void complete(int cost,int weight) { for(int i=cost;i<=cost;i++) dp[i]=max(dp[i],dp[i-cost]+weight); } void multi(int cost,int weight,int cnt) { if(total<=cnt*cost) { complete(cost,weight); return; } int k=1; while(k<=cnt) { zero(k*cnt,k*weight); cnt=cnt-k; k=2*k; } zero(cnt*cost,cnt*weight); } int main() { while(~scanf("%d",&n)) { if(n==-1) break; total=0; sum=0; for(int i=0;i<n;i++) { scanf("%d%d",&value[i],&num[i]); total+=(value[i]*num[i]); } printf("%d \n",total); sum=total; total/=2; memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) { multi(value[i],value[i],num[i]); } printf("%d %d\n",sum-dp[total],dp[total]); } return 0; }
也可以用01背包搞 就是把相同价值的一种看成是多件
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,tot,a,b,sum; int dp[255000],val[600]; int main() { while(~scanf("%d",&n),n>0) { sum=0; tot=0; for(int i=0;i<n;i++) { scanf("%d%d",&a,&b); while(b--) { val[tot++]=a; sum+=a; } } memset(dp,0,sizeof(dp)); for(int i=0;i<tot;i++) { for(int j=sum/2;j>=val[i];j--) { dp[j]=max(dp[j],dp[j-val[i]]+val[i]); } } printf("%d %d\n",sum-dp[sum/2],dp[sum/2]); } return 0; }
7. 01背包求第k优解
hdu2639bone collector II【第K优解】
作为一个正常的背包,我们必须做的两重循环是一定要写的,接下来就是维护最优解到第k优解的的数组。个人觉得这个题最最值得学习的就是维护数组的写法:
首先每次想加入新物品的时候,正常来说我们有两个值:之前的dp和加入新物品时的值,然而我们现在也需要考虑第k优解的问题,所以用两个临时的数组倒一下,再用这两个数组合并到一起存储到最终的dp数组中
/************ hdu2639 2015.10.20 109MS 5548K 1226B ************/ #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[1009][1009]; int value[109],cost[109]; int n,k,v,t; int a[1009],b[1009]; int main() { while(~scanf("%d",&t)) { while(t--) { scanf("%d%d%d",&n,&v,&k); for(int i=1;i<=n;i++) scanf("%d",&value[i]); for(int i=1;i<=n;i++) scanf("%d",&cost[i]); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) for(int j=v;j>=cost[i];j--) { for(int m=1;m<=k;m++) { a[m]=dp[j][m]; b[m]=dp[j-cost[i]][m]+value[i]; } int x=1,y=1,w=1; a[k+1]=-1;b[k+1]=-1; while((w<=k)&&(x<=k||y<=k)) { if(a[x]<b[y]) dp[j][w]=b[y++]; else dp[j][w]=a[x++]; if(w==1||dp[j][w-1]!=dp[j][w]) w++; } } printf("%d\n",dp[v][k]); } } return 0; }
8.
hdu3466Proud Merchants【至少需要Qi才能买Pi】
hdu5188zhx and contest [01背包至少li才能。。。]
他俩是一类问题,只是表述有区别而已:手头的钱数大于等于Qi才可以放当前商品,后一个题由于没有明显的给上限,所以手动维护一个sum是所占体积和与至少需要体积最大值这两个数中最大值。有必要说一下排序函数的由来,设A:p1,q1 B:p2,q2,如果先A后B,则至少需要p1+q2的容量,如果先B后A,至少需要p2+q1的容量,那么就是p1+q2 > p2+q1,变形之后就是q1-p1 < q2-p2。
#include<stdio.h> #include<algorithm> #include<iostream> using namespace std; const int MAXN=5005; int dp[MAXN]; struct Node { int p,q,v; }node[505]; bool cmp(Node a,Node b) { return (a.q-a.p)<(b.q-b.p); } int main() { int n,m; int i,j; int p,q,v; while(scanf("%d%d",&n,&m)!=EOF) { for(i=0;i<=m;i++) dp[i]=0; for(i=0;i<n;i++) { scanf("%d%d%d",&node[i].p,&node[i].q,&node[i].v); } sort(node,node+n,cmp); for(i=0;i<n;i++) { for(j=m;j>=node[i].p;j--) { if(j>=node[i].q) dp[j]=max(dp[j],dp[j-node[i].p]+node[i].v); } } int ans=0; for(i=1;i<=m;i++) if(ans<dp[i]) ans=dp[i]; printf("%d\n",ans); } return 0; }
#include<stdio.h> #include<algorithm> #include<iostream> #include<cstring> using namespace std; const int MAXN=4000009; int dp[MAXN]; struct Node { int p,q,v; }node[35]; bool cmp(Node a,Node b) { return (a.q-a.p)<(b.q-b.p); } int main() { // freopen("cin.txt","r",stdin); int n,w; int i,j; int p,q,v; while(scanf("%d%d",&n,&w)!=EOF) { memset(dp,0,sizeof(dp)); int sum=0,up=0; for(i=0;i<n;i++) { scanf("%d%d%d",&node[i].p,&node[i].v,&node[i].q); sum+=node[i].p; up=max(up,node[i].q); } up=max(up,sum); sort(node,node+n,cmp); for(i=0;i<n;i++) { for(j=up;j>=node[i].p;j--) { if(j>=node[i].q) dp[j]=max(dp[j],dp[j-node[i].p]+node[i].v); } } int ans=0; for(i=1;i<=up;i++) if(w<=dp[i]) {ans=i;break;} if(ans) printf("%d\n",ans); else puts("zhx is naive!"); } return 0; } <span style="font-size:14px;"> </span>
9.负数处理+两个总和的和最大
poj2184Cow Exhibition【01背包 负数】
题意:每只奶牛都有一个聪明值和有趣值,最终想求得选取一些奶牛使得这两个的总和最大,而且聪明值的和与有趣值的总和分别都不能为负
虽然N只有100,但是也不可以逐个枚举啊,可以选取两个值中的一个作为背包容量,另一个作为总价值。负数的处理是另外与价值数组更新时维护一个数组表示当前数组内的元素,因为+1000了嘛,要记着加了几个。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <iostream> #include <algorithm> #include <map> #define LL long long using namespace std; const int N = 110; const int M = 200010; const int NN = 1000; const int INF = 10000000; int w[N],v[N],sum; int dp[M], cnt[M]; int main() { // freopen("1.txt","r",stdin); int n; while(~scanf("%d",&n)) { sum=0; for(int i=1;i<=n;i++) { scanf("%d%d",&v[i],&w[i]); if(v[i]<0&&w[i]<0){i--,n--;continue;} v[i]+=1000; sum+=v[i]; } memset(dp,-0x3f3f3f3f,sizeof(dp)); dp[0]=0; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) { for(int j=sum;j>=v[i];j--) { if(dp[j-v[i]]+w[i]-(cnt[j-v[i]]+1)*1000>dp[j]-cnt[j]*1000) { cnt[j]=cnt[j-v[i]]+1; dp[j]=dp[j-v[i]]+w[i]; } } } int ans=0; for(int i=0;i<=sum;i++) { if(i-cnt[i]*1000>0&&dp[i]>0)// { ans=max(ans,dp[i]-cnt[i]*1000+i); } } printf("%d\n",ans); } return 0; }
10.
hdu2159FATE【二维完全背包】
xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能升掉这最后一级吗?
既然是二维背包那么二维分别是现有忍耐度和打怪的个数
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[105][105],k,n,m,s,a[105],b[105]; int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d%d%d",&n,&m,&k,&s)) { for(int i=1;i<=k;i++) scanf("%d%d",&a[i],&b[i]); // memset(f,0,sizeof(f)); memset(dp,0,sizeof(dp)); for(int i=1;i<=k;i++) { for(int j=b[i];j<=m;j++) { for(int l=1;l<=s;l++) { dp[j][l]=max(dp[j][l],dp[j-b[i]][l-1]+a[i]); } } } int flag=0; for(int i=1;i<=m;i++) { for(int j=1;j<=s;j++) { if(dp[i][j]>=n) { printf("%d\n",m-i); flag=1; goto loop; } } } loop : if(!flag) printf("-1\n"); } return 0; }
11.二维完全背包分面积,给出小块区域的价值,求最大值
hdu3127WHUgirls【二维完全背包】
啥都不说,直接上图==
/************* hdu3127 2015.10.27-2015.10.29 296MS 5536K 1130 B ************/ #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,X,Y,t; int dp[1005][1005],x[12],y[12],c[12]; int max(int a,int b,int c) { if(a>b) { if(c>a) return c; return a; } if(c>b) return c; return b; } int main() { //freopen("cin.txt","r",stdin); cin>>t; while(t--) { cin>>n>>X>>Y; for(int i=1;i<=n;i++) cin>>x[i]>>y[i]>>c[i]; memset(dp,0,sizeof(dp)); for(int k=1;k<=n;k++) { for(int i=0;i<=X;i++) { for(int j=0;j<=Y;j++) { if(i>=x[k]&&j>=y[k]) dp[i][j]=max(dp[i][j],dp[i-x[k]][j]+dp[x[k]][j-y[k]]+c[k],dp[i][j-y[k]]+dp[i-x[k]][y[k]]+c[k]); if(i>=y[k]&&j>=x[k]) dp[i][j]=max(dp[i][j],dp[i-y[k]][j]+dp[y[k]][j-x[k]]+c[k],dp[i][j-x[k]]+dp[i-y[k]][x[k]]+c[k]); } } } cout<<dp[X][Y]<<endl; } return 0; }
12.
hdu1248寒冰王座【完全背包入门题】
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[10005],t,n; int value[4]={0,150,200,350}; int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++) dp[i]=0; for(int i=1;i<=3;i++) for(int j=value[i];j<=n;j++) { dp[j]=max(dp[j-value[i]]+value[i],dp[j]); } printf("%d\n",n-dp[n]); } return 0; }
13.
hdu2191悼念512汶川大地震遇难同胞——珍惜现在,感恩生活【多重背包模板题】
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int dp[1050],c[1050],w[1050],m[1050]; int n,M,t; void zeropack(int cost,int weight) { for(int i=n;i>=cost;i--) dp[i]=max(dp[i-cost]+weight,dp[i]); } void completepack(int cost,int weight) { for(int i=cost;i<=n;i++) dp[i]=max(dp[i-cost]+weight,dp[i]); } void multipack(int cost,int weight,int num) { if(num*cost>=n) { completepack(cost,weight); return; } int k=1; while(k<num) { zeropack(k*cost,k*weight); num-=k; k*=2; } zeropack(cost*num,weight*num); } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&M); for(int i=1;i<=M;i++) scanf("%d%d%d",&c[i],&w[i],&m[i]); memset(dp,0,sizeof(dp)); for(int i=1;i<=M;i++) { multipack(c[i],w[i],m[i]); } printf("%d\n",dp[n]); } return 0; }
14.求一共可以凑出的方法数
hdu2844Coins【多重背包】
因为体积和价值是同一个数,那么dp值很容易想到就是1-max遍历值==i的总数++
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int dp[100007],n,m,a[105],c[105],count; void zeropack(int cost,int value) { for(int i=m;i>=cost;i--) dp[i]=max(dp[i],dp[i-cost]+value); // cout<<"01"<<endl; } void completepack(int cost,int value) { for(int i=cost;i<=m;i++) dp[i]=max(dp[i],dp[i-cost]+value); //cout<<"complete"<<endl; } void multipack(int cost,int value,int num) { if(num*cost>=m) { completepack(cost,value); return; } int k=1; while(k<num) { zeropack(k*cost,k*value); num-=k; k*=2; } zeropack(num*cost,num*value); } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d",&n,&m)) { if(n==0&&m==0) break; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&c[i]); memset(dp,0,sizeof(dp)); count=0; for(int i=1;i<=n;i++) { multipack(a[i],a[i],c[i]); } for(int i=1;i<=m;i++) { if(dp[i]==i) count++; //printf("dp[]=%d\n",dp[i]); } printf("%d\n",count); } return 0; }
15.多重背包模板题
hdu1059Dividing【多重背包】
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int dp[90000],value[10],cost[10],num[10],v; void zeropack(int cost,int value) { for(int i=v;i>=cost;i--) dp[i]=max(dp[i],dp[i-cost]+value); // printf("zero "); } void complete(int cost,int value) { for(int i=cost;i<=v;i++) dp[i]=max(dp[i],dp[i-cost]+value); //printf("complete "); } void multipack(int cost,int value,int num) { if(num==0) return; if(cost*num>=v) { complete(cost,value); return; } int k=1; while(k<num) { zeropack(k*cost,k*value); num-=k; k*=2; } zeropack(num*cost,num*value); } int main() { //freopen("cin.txt","r",stdin); int cnt=1; while(~scanf("%d%d%d%d%d%d",&num[1],&num[2],&num[3],&num[4],&num[5],&num[6])) { for(int i=1;i<=6;i++) value[i]=i; for(int i=1;i<=6;i++) cost[i]=i; if(num[1]==0&&num[2]==0&&num[3]==0&&num[4]==0&&num[5]==0&&num[6]==0) break; v=num[1]+num[2]*2+num[3]*3+num[4]*4+num[5]*5+num[6]*6; //for(int i=1;i<=6;i++) if(!num[i]) {value[i]=0;cost[i]=0;} //for(int i=1;i<=6;i++) printf("%d ",num[i]); if(v&1) { printf("Collection #%d:\nCan't be divided.\n\n",cnt++); continue; } v/=2; memset(dp,0,sizeof(dp)); for(int i=1;i<=6;i++) { if(num[i]==0) continue; //printf("i=%d ",i); multipack(cost[i],value[i],num[i]); // printf("i=%d ",i); // for(int j=cost[i];j<=v;j++)printf("i=%d j=%d dp[j]=%d ",i,j,dp[j]); // printf("\n"); } //printf("%d\n",dp[v]); // for(int i=1;i<=6;i++) printf("%d ",dp[i]); if(dp[v]!=v) printf("Collection #%d:\nCan't be divided.\n\n",cnt++); else printf("Collection #%d:\nCan be divided.\n\n",cnt++); } return 0; }
16.给钱加找钱最少个数和
hdu3591The trouble of Xiaoqian【多重背包】
由于手里的各种钱个数有限,所以多重背包跑一遍,注意每个dp的下标都是实际可以达到的!再给找的钱跑一遍完全背包,两个循环找最小值和
#include <iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 20000 int dp1[maxn+6],value[105],num[105],t,n,dp2[maxn+6];//钱币个数 int f1[maxn+6],f2[maxn+6];//钱数 void zeropack(int cost,int value,int k) { for(int i=maxn;i>=cost;i--) { if(dp1[i-cost]!=-1&&(dp1[i-cost]+k<dp1[i]||dp1[i]==-1)) dp1[i]=dp1[i-cost]+k; } // cout<<"01"<<endl; } void completepack(int cost,int value) { for(int i=cost;i<=maxn;i++) { if(dp1[i-cost]!=-1&&(dp1[i-cost]+1<dp1[i]||dp1[i]==-1)) dp1[i]=dp1[i-cost]+1; // f1[i]=max(f1[i],f1[i-cost]+value); } //cout<<"complete"<<endl; } void multipack(int cost,int value,int num) { if(num*cost>=maxn) { completepack(cost,value); return; } int k=1; while(k<num) { zeropack(k*cost,k*value,k); num-=k; k*=2; } zeropack(num*cost,num*value,k); } int main() { // freopen("cin.txt","r",stdin); int cnt=1; while(~scanf("%d%d",&n,&t)) { if(t==0&&n==0) break; for(int i=1;i<=n;i++) scanf("%d",&value[i]); for(int i=1;i<=n;i++) scanf("%d",&num[i]); memset(dp2,-1,sizeof(dp2)); memset(dp1,-1,sizeof(dp1)); memset(f1,0,sizeof(f1)); memset(f2,0,sizeof(f2)); dp2[0]=0;dp1[0]=0; for(int i=1;i<=n;i++) for(int j=value[i];j<=maxn;j++) { if(dp2[j-value[i]]!=-1&&(dp2[j]<dp2[j-value[i]]+1||dp2[j]==-1)) dp2[j]=dp2[j-value[i]]+1; // f2[j]=max(f2[j],f2[j-value[i]]+value[i]); } for(int i=1;i<=n;i++) { multipack(value[i],value[i],num[i]); } // for(int i=70;i<=80;i++) printf("f1=%d f2=%d dp1=%d dp2=%d\n",f1[i],f2[i],dp1[i],dp2[i]); int minn=0x3f3f3f3f; for(int i=t;i<=maxn;i++) { //if(f1[i]!=i||f2[i-t]!=i-t) continue; if(dp1[i]!=-1&&dp2[i-t]!=-1) minn=min(minn,dp1[i]+dp2[i-t]); } if(minn!=0x3f3f3f3f) printf("Case %d: %d\n",cnt++,minn); else printf("Case %d: -1\n",cnt++); } return 0; }
17.
hdu3535AreYouBusy【分组背包综合题】
这是一道综合性的背包问题。题目给出了多组工作,每组工作的选择规则不同,有些组至少要选一项,有些组最多选一项,有些组任意选。 这道题花了很长时间,需要深入理解状态转移才能够明白。数组dp[i][j],表示第i组,时间剩余为j时的快乐值。每得到一组工作就进行一次DP,所以dp[i]为第i组的结果。下面对三种情况进行讨论。 第一类,至少选一项,即必须要选,那么在开始时,对于这一组的dp的初值,应该全部赋为负无穷,这样才能保证不会出现都不选的情况。状态转移方程为dp[i][k]=max{ dp[i][k],dp[i-1][k-cost[j]]+val[k],dp[i][k-cost[j]]+val[j] }。dp[i][k]是不选择当前工作;dp[i-1][k-cost[j]]+val[k]是选择当前工作,但是是第一次在本组中选,由于开始将该组dp赋为了负无穷,所以第一次取时,必须由上一组的结果推知,这样才能保证得到全局最优解;dp[i][k-cost[j]]+val[j]表示选择当前工作,并且不是第一次取。 第二类,最多选一项,即要么不选,一旦选,只能是第一次选。所以状态转移方程为dp[i][k]=max{ dp[i][k],dp[i-1][k-cost[j]]+val[k]}。由于要保证得到全局最优解,所以在该组DP开始以前,应该将上一组的DP结果先复制到这一组的dp[i]数组里,因为这一组的数据是在上一组数据的基础上进行更新的。 第三类,任意选,即不论选不选,选几次都可以,显然状态转移方程为dp[i][k]=max{ dp[i][k],dp[i-1][k-cost[j]]+val[k],dp[i][k-cost[j]]+val[j] }。同样要保证为得到全局最优解,先复制上一组解。
#include<stdio.h> #include<string.h> #define INF 1000000 #define MAX_LIMT 110 int get_max(int,int); int main() { int n,t; while(scanf("%d%d",&n,&t)!=EOF) { int i,j,k,dp[MAX_LIMT][MAX_LIMT]; memset(dp,0,sizeof(dp)); for(i=1;i<=n;i++) { int m,s,cost[MAX_LIMT],val[MAX_LIMT]; scanf("%d%d",&m,&s); for(j=1;j<=m;j++) { scanf("%d%d",&cost[j],&val[j]); } if(s==0) { for(j=0;j<=t;j++) { dp[i][j]=-INF; } for(j=1;j<=m;j++) { for(k=t;k>=cost[j];k--) { dp[i][k]=get_max( dp[i][k],dp[i][k-cost[j]]+val[j]); dp[i][k]=get_max( dp[i][k],dp[i-1][k-cost[j]]+val[j]); } } } else if(s==1) { for(j=0;j<=t;j++) { dp[i][j]=dp[i-1][j]; } for(j=1;j<=m;j++) { for(k=t;k>=cost[j];k--) { dp[i][k]=get_max(dp[i][k],dp[i-1][k-cost[j]]+val[j]); } } } else { for(j=0;j<=t;j++) { dp[i][j]=dp[i-1][j]; } for(j=1;j<=m;j++) { for(k=t;k>=cost[j];k--) { dp[i][k]=get_max( dp[i][k],dp[i][k-cost[j]]+val[j]); dp[i][k]=get_max( dp[i][k],dp[i-1][k-cost[j]]+val[j]); } } } } dp[n][t]=get_max(dp[n][t],-1); printf("%d\n",dp[n][t]); } return 0; } int get_max(int x,int y) { return x>y?x:y; }
18.2012 Multi-University Training Contest 9
hdu4381Grid【微复杂的背包】
1 ai xi :You can choose any xi black boxes in interval [1,ai], and color them white;
2 ai xi :You can choose any xi black boxes in interval [ai,n], and color them white;
求最终能有最多多少白色格子。
由于题中没说到底涂哪些,我们认为都是连着涂的,那么我们就可以将给出的数据转化成可涂的长度,分别用两个数组计算当前长度范围最多可涂个数的最少次数,求和取最小值即可
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #include <cmath> using namespace std; const int maxn = 2005; struct node{ int aa,cor; node(){} node(int _aa,int _cor){ aa = _aa; cor = _cor; } }x1[maxn],x2[maxn]; int n,m; int dp1[maxn],dp2[maxn]; int sumx1,sumx2; bool cmp(const node &p,const node &q){ return p.aa < q.aa; } int min(int a,int b) { return a<b?a:b; } int main() { int cas,ta=1; scanf("%d",&cas); while(cas--){ int i,j; scanf("%d%d",&n,&m); sumx1 = sumx2 = 1; for(i=0; i<m; i++){ int key,yy,z; scanf("%d%d%d",&key,&yy,&z); if(key == 1){ x1[sumx1++] = node(yy,z); }else{ x2[sumx2++] = node(n+1-yy,z); } } sort(x1+1,x1+sumx1,cmp); sort(x2+1,x2+sumx2,cmp); memset(dp1,0x3f,sizeof(dp1)); memset(dp2,0x3f,sizeof(dp2)); dp1[0] = dp2[0] = 0; for(i=1; i<sumx1; i++){ for(j=x1[i].aa; j>=x1[i].cor; j--){ dp1[j] = min(dp1[j],dp1[j-x1[i].cor]+1); } } for(i=1; i<sumx2; i++){ for(j=x2[i].aa; j>=x2[i].cor; j--){ dp2[j] = min(dp2[j],dp2[j-x2[i].cor]+1); } } int ans = 0,anssum = 0,tmp; for(i=1; i<=n; i++){ for(j=0; j<=i; j++){ tmp = dp1[j] + dp2[i-j]; if(tmp <= m){ if(ans != i){ ans = i; anssum = tmp; }else if(tmp < anssum){ anssum = tmp; } } } } printf("Case %d: %d %d\n",ta++,ans,anssum); } return 0; }
19。多重背包至多ai才可以放
poj2392Space Elevator【多重背包】
和“至少”的情况一样,都要先排序
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[40004]; int n,M,t; struct node { int cost,weight,num,height; }num[300]; bool cmp(node a,node b) { return a.height<b.height; } void zeropack(int cost,int weight,int height) { for(int i=height;i>=cost;i--) dp[i]=max(dp[i-cost]+weight,dp[i]); } void completepack(int cost,int weight,int height) { for(int i=cost;i<=height;i++) dp[i]=max(dp[i-cost]+weight,dp[i]); } void multipack(int cost,int weight,int num,int height) { if(num*cost>=height) { completepack(cost,weight,height); return; } int k=1; while(k<num) { zeropack(k*cost,k*weight,height); num-=k; k*=2; } zeropack(cost*num,weight*num,height); } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d",&n)) { int maxn=0; for(int i=1;i<=n;i++) { scanf("%d%d%d",&num[i].cost,&num[i].height,&num[i].num); num[i].weight=num[i].cost; if(maxn<num[i].height) maxn=num[i].height; } sort(num+1,num+1+n,cmp); // printf("hei=%d ",maxn); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) multipack(num[i].cost,num[i].weight,num[i].num,num[i].height); int tmp=0; for(int i=1;i<=maxn;i++) { if(tmp<dp[i]) tmp=dp[i]; //printf("%d ",dp[i]); } printf("%d\n",tmp); } return 0; }
20.
hdu1712ACboy needs your help【分组背包入门题】
第一层是不同科间的循环,第二层的循环是v从已知最大天数到0,第三层是组内循环也就是每个课程选几天的付出
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int num[110][110],n,m; int dp[110]; int max(int a,int b){if(a>b)return a;return b;} int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d",&n,&m)) { if(m==0&&n==0)break; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&num[i][j]); memset(dp,0,sizeof(dp)); for(int k=1;k<=n;k++)//the type of course for(int v=m;v>=0;v--) for(int j=1;j<=v;j++) dp[v]=max(dp[v],dp[v-j]+num[k][j]); printf("%d\n",dp[m]); } return 0; }
21.分组背包每组至少去取一个
由于限制了每组个数,我们需要加一维表示组别,而且前一状态不可没被填充
hdu3033I love sneakers!【分组背包】每组至少取一个
/*********** hdu3033 2016.3.13 93MS 5888K 1238B G++ ***********/ #include <iostream> #include<cstdio> #include<cstring> using namespace std; struct node { int u,v; }g[15][110]; int num[15],dp[110][10005]; int n,m,k,a,b,c; int max(int a,int b){if(a>b)return a;return b;} #define inf 0x3f3f3f3f int main() { while(~scanf("%d%d%d",&n,&m,&k)) { memset(num,0,sizeof(num)); for(int i=1;i<=n;i++) { scanf("%d%d%d",&a,&b,&c); num[a]++; g[a][num[a]].u=b; g[a][num[a]].v=c; } memset(dp,-inf,sizeof(dp)); //有的时候memset赋值-inf会出错 memset(dp[0],0,sizeof(dp[0])); for(int i=1;i<=k;i++) { for(int j=1;j<=num[i];j++) { for(int v=m;v>=g[i][j].u;v--) { //if(dp[i][v-g[i][j].u]!=-1) dp[i][v]=max(dp[i][v],dp[i][v-g[i][j].u]+g[i][j].v); //if(dp[i-1][v-g[i][j].u]!=-1) dp[i][v]=max(dp[i][v],dp[i-1][v-g[i][j].u]+g[i][j].v); } } } if(dp[k][m]<0)printf("Impossible\n"); else printf("%d\n",dp[k][m]); } return 0; }
22.
hdu3469Watch The Movie【分组背包】二维dp数组
小姑娘开学前晚上看电影只能L分钟,有N个不同的电影可以选择,傲娇老板只恰好卖M张蝶,给出每部电影时长和快乐值,问最大的快乐值是多少
组别是n种电影,组内是电影的个数,dp一维表示电影个数,二维表示时间长度
#include <iostream> #include<cstdio> #include<cstring> using namespace std; #define inf 0x3f3f3f3f int dp[102][1005],cost[102],value[102],t,n,m,l; int max(int a,int b){if(a>b)return a;return b;} int main() { // freopen("cin.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&l); for(int i=1;i<=n;i++)scanf("%d%d",&cost[i],&value[i]); for(int i=0;i<=m;i++) for(int j=0;j<=l;j++) dp[i][j]=-0x3f3f3f3f; dp[m][l]=dp[0][0]=0; for(int i=1;i<=n;i++) for(int j=m;j>0;j--) for(int k=cost[i];k<=l;k++) dp[j][k]=max(dp[j][k],dp[j-1][k-cost[i]]+value[i]);//printf("i=%d,j=%d,k=%d,dp=%d\n",i,j,k,dp[j][k]); for(int i=0;i<l;i++)dp[m][l]=max(dp[m][l],dp[m][i]) ; printf("%d\n",dp[m][l]); } return 0; }
23.01背包方法数使得是剩下的钱数不能大于没买的物品的最小值
poj3093Margaritas on the River Walk【01背包计算方法数】
//思路:先对n个体积进行从小到大的排序,然后枚举i作为剩余物品中体积最小为v,dp[j]为方案数(其中j为当前体积).那么可以分析对于大于i的, //很显然是可以放进背包的,又因为i为剩余的物品,所以不放进去;对于大于i的物品则进行背包的可行方案的统计.然后计算{Fn[j]}之和。 #include <stdio.h> #include <string.h> #include <stdlib.h> #define M 10000 #define max(a, b) (a > b ? a : b) int data[M], dp[M]; int n, c; int cmp(const void *a, const void *b) { return *(int *)a - *(int *)b; } int main() { //freopen("1.txt", "r", stdin); int i, j, k, test, sum, ans, num = 1; scanf("%d", &test); while (test--) { scanf("%d%d", &n, &c); for (i = 1; i <= n; i++) { scanf("%d", &data[i]); } qsort(data + 1, n, sizeof(data[1]), cmp); memset(dp, 0, sizeof(dp)); for (i = 1, sum = 0, ans = 0; i <= n; i++) { memset(dp, 0, sizeof(dp)); dp[sum] = 1; for (j = i + 1; j <= n; j++) { for (k = c; k >= data[j] + sum; k--) { dp[k] += dp[k - data[j]]; } } for (j = c; j >= max(c - data[i] + 1, 1); j--) //c-data[i]+1 其中+1是因为下标以1开始 { if (j >= sum) { ans += dp[j]; } } sum += data[i]; } printf("%d %d\n", num++, ans); } return 0; }
24.
hdu3236Gift Hunting【二维01背包】
题意:给女朋友买一堆礼物,每个礼物有一个快乐值,有n个备选的,其中有一部分是必买的,总共有两张支票,价值v1,v2,花钱少了不给退,两张支票不能凑在一起用,而且有一个礼物是免费的,问女友最多可以多快乐?
做法:这个题的处理方法是再加一维dp表示是否用过这个免费的机会(貌似之前树形dp做过吧)之后,dp转移是很容易可以想到的==
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int cost1[309],hap1[309],cost0[309],hap0[309],dp[505][55][2],n,v1,v2; int main() { // freopen("cin.txt","r",stdin); //freopen("out.txt","w",stdout); int cas=1; while(~scanf("%d%d%d",&v1,&v2,&n)) { if(v1==0&&v2==0&&n==0)break; memset(dp,0,sizeof(dp)); int l0=0,l1=0,maxh=0,maxn; for(int i=0;i<n;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(c==1) { cost1[l1]=a; hap1[l1++]=b; maxh+=b; } if(c==0) { cost0[l0]=a; hap0[l0++]=b; } } for(int i=0;i<l1;i++) { for(int j=v1;j>=0;j--) { for(int k=v2;k>=0;k--) { dp[j][k][1]=max(dp[j][k][1],dp[j][k][0]+hap1[i]); if(j>=cost1[i]) dp[j][k][0]=max(dp[j][k][0],dp[j-cost1[i]][k][0]+hap1[i]), dp[j][k][1]=max(dp[j][k][1],dp[j-cost1[i]][k][1]+hap1[i]); if(k>=cost1[i]) dp[j][k][0]=max(dp[j][k][0],dp[j][k-cost1[i]][0]+hap1[i]), dp[j][k][1]=max(dp[j][k][1],dp[j][k-cost1[i]][1]+hap1[i]); // printf("i=%d,j=%d,k=%d,dp0=%d,dp1=%d\n",i,j,k,dp[j][k][0],dp[j][k][1]); } } } if(dp[v1][v2][1]<maxh)maxn=-1; else { for(int i=0;i<l0;i++) { for(int j=v1;j>=0;j--) { for(int k=v2;k>=0;k--) { if(dp[j][k][0]>=maxh) dp[j][k][1]=max(dp[j][k][1],dp[j][k][0]+hap0[i]); if(j>=cost0[i]&&dp[j-cost0[i]][k][0]>=maxh) dp[j][k][0]=max(dp[j][k][0],dp[j-cost0[i]][k][0]+hap0[i]); if(j>=cost0[i]&&dp[j-cost0[i]][k][1]>=maxh) dp[j][k][1]=max(dp[j][k][1],dp[j-cost0[i]][k][1]+hap0[i]); if(k>=cost0[i]&&dp[j][k-cost0[i]][0]>=maxh) dp[j][k][0]=max(dp[j][k][0],dp[j][k-cost0[i]][0]+hap0[i]); if(k>=cost0[i]&&dp[j][k-cost0[i]][1]>=maxh) dp[j][k][1]=max(dp[j][k][1],dp[j][k-cost0[i]][1]+hap0[i]); // printf("i=%d,j=%d,k=%d,dp0=%d,dp1=%d\n",i,j,k,dp[j][k][0],dp[j][k][1]); } } } maxn=dp[v1][v2][1]; } printf("Case %d: %d\n\n",cas++,maxn); } return 0; }
25.
poj1276Cash Machine【多重背包模板题】
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int dp[100009],c[100009],w[100009],m[100009]; int n,M,t; void zeropack(int cost,int weight) { for(int i=M;i>=cost;i--) dp[i]=max(dp[i-cost]+weight,dp[i]); } void completepack(int cost,int weight) { for(int i=cost;i<=M;i++) dp[i]=max(dp[i-cost]+weight,dp[i]); } void multipack(int cost,int weight,int num) { if(num*cost>=M) { completepack(cost,weight); return; } int k=1; while(k<num) { zeropack(k*cost,k*weight); num-=k; k*=2; } zeropack(cost*num,weight*num); } int main() { //freopen("cin.txt","r",stdin); while(~scanf("%d%d",&M,&n)) { for(int i=1;i<=n;i++){scanf("%d%d",&m[i],&c[i]);w[i]=c[i];} memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { multipack(c[i],w[i],m[i]); } printf("%d\n",dp[M]); } return 0; }
26.
poj1837Balance【二维01背包方法数——天平平衡】
题意:已知砝码(都只有一个)和天平左右挂钩的位置,问有多少种使得天平平衡的方法
做法:很需要那维表示当前物品序号,因为转移方程是dp[i][j]+=dp[i-1][j-pos[k]*weight[i]];
负数的处理,十分纠结于7500是中间值,要是没到这么大的话怎么办啊==看代码
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int dp[30][20009]; int c,g,pos[30],weight[30]; int main() { // freopen("cin.txt","r",stdin); // freopen("out.txt","w",stdout); while(~scanf("%d%d",&c,&g)) { for(int i=1;i<=c;i++) { scanf("%d",&pos[i]); //cc[i]+=15; } for(int i=1;i<=g;i++)scanf("%d",&weight[i]); memset(dp,0,sizeof(dp)); dp[0][10000]=1; int maxn=20000; for(int i=1;i<=g;i++) for(int j=0;j<=maxn;j++) for(int k=1;k<=c;k++) if(j>=pos[k]*weight[i]) { dp[i][j]+=dp[i-1][j-pos[k]*weight[i]]; // printf("i=%d,j=%d,k=%d,dp=%d\n",i,j,k,dp[i][j]); } printf("%d\n",dp[g][10000]); } return 0; }