背包训练
背包问题初始值:
恰好装满: d[0]=0, d[1].d[2].d[3]....d[n-1]= 负无穷;
尽可能的满: d[0].d[1].d[2].....d[n-1] = 0;
背包九讲:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么 任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。
01背包:
多个物品对于每个物品只能选择一次且每个物品两种选择: 放入背包(背包容量减少,包含的价值增加);不放(背包容量和价值就等于上一个物品的状态)。
状态:dp[j]=max( dp[j] , dp[j-w[i]] + v[i] ); //j=w[i]...v
完全背包:
多个物品对于每种物品可以选择多次。
可以通过转化成01背包来求解,类似于:dp[j]=max( dp[j] , dp[j-k*W[i]] + k*V[i] ); 这里K 为倍数;
有时类似于:dp[k]=max( dp[k] , dp[k-w[j]] + v[j] ); //在K容量下能获得最大价值,j为一类物品
有时类似于:f[v]=max( f[v] , f[v-cost]+weight ); //V=0...n
二维费用背包:
对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有 一个可付出的最大值(背包容量)。
第 k 最优解:
1. HDU 2546 01背包
问题分析:
a) 若饭卡钱数小于5则结果为:饭卡钱数。否则找出若干菜品的价格和(不包括最大价格菜品)最趋于饭卡价格-5,最大价格菜品留着最后减去(这样才能使得最终饭卡钱数最少)。
b) 饭卡钱数-5 为背包容量,找出最大价格菜品(这里用sort排序,最后一个为最大价格),一直装入背包。结果为:饭卡钱数-最大价格-dp[饭卡钱数-5]
dp[i] 表示:容量为 i 时 所拥有的最大钱数。在该题中表示 当钱数为 i 时 最接近 i 钱数的钱数。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int dp[10000+6],a[1005]; 4 int main() 5 { 6 int n,m; 7 while(scanf("%d",&n)!=EOF&&n) 8 { 9 memset(dp,0,sizeof(dp)); 10 memset(a,0,sizeof(a)); 11 for(int i=0; i<n; i++) 12 scanf("%d",&a[i]); 13 scanf("%d",&m); 14 sort(a,a+n); 15 for(int i=0; i<n-1; i++) 16 for(int j=m-5; j>=a[i]; j--) 17 dp[j]=max(dp[j],dp[j-a[i]]+a[i]); 18 if(m<5) 19 printf("%d\n",m); 20 else 21 printf("%d\n",m-dp[m-5]-a[n-1]); 22 } 23 return 0; 24 }
2. HDU 1114 完全背包
问题分析:
最终要使得 放入的钱的重量(放钱后的重量-空罐的重量时) 钱数最小。
dp[i] 表示:容量为 i 时 所拥有的最小钱数。在该题中表示 当容量为 钱的重量(=放钱后的重量-空罐的重量)时最少价值。
注意在初始化时 dp[0]=0,没有重量即没有价值。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int dp[10000+6],v[505],w[505]; 4 int main() 5 { 6 int T,s,e,tn,i,j; 7 scanf("%d",&T); 8 while(T--) 9 { 10 memset(v,0,sizeof(v)); 11 memset(w,0,sizeof(w)); 12 scanf("%d%d%d",&s,&e,&tn); 13 for(i=0; i<e; ++i) 14 dp[i]=1<<30; 15 dp[0]=0; 16 for(i=1; i<=tn; ++i) 17 scanf("%d%d",&v[i],&w[i]); 18 for(i=1; i<=tn; ++i) 19 for(j=w[i]; j<=e-s; ++j) 20 dp[j]=min(dp[j],dp[j-w[i]]+v[i]); 21 if(dp[e-s]==1<<30) 22 printf("This is impossible.\n"); 23 else 24 printf("The minimum amount of money in the piggy-bank is %d.\n",dp[e-s]); 25 } 26 return 0; 27 }
3. HDU 2191 完全背包
问题分析:
dp[i] 表示 在容量为 i 时 能放的最大重量。在该题中表示 当钱为 i 时 能买得最多大米的重量。
这里注意 可买的大米袋数 <= 商家拥有的袋数
1 #include<bits/stdc++.h> 2 using namespace std; 3 int v[105],w[105],num[105],dp[105]; 4 int main() 5 { 6 int T,n,m,i,j,k; 7 scanf("%d",&T); 8 while(T--) 9 { 10 memset(dp,0,sizeof(dp)); 11 scanf("%d%d",&n,&m); 12 for(i=0; i<m; i++) 13 scanf("%d%d%d",&v[i],&w[i],&num[i]); 14 for(i=0; i<m; i++) 15 for(j=n; j>=v[i]; j--) 16 for(k=1; k<=j/v[i]; k++) 17 if(k<=num[i]) 18 dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]); 19 printf("%d\n",dp[n]); 20 } 21 return 0; 22 }
4. HDU 2602 01背包
问题分析:01背包练习基本题目*
1 #include<bits/stdc++.h> 2 using namespace std; 3 int v[1005],w[1005],dp[1005]; 4 int main() 5 { 6 int T,n,m,i,j,k; 7 scanf("%d",&T); 8 while(T--) 9 { 10 memset(dp,0,sizeof(dp)); 11 scanf("%d%d",&n,&m); 12 13 for(i=0; i<n; i++) 14 scanf("%d",&v[i]); 15 for(i=0; i<n; i++) 16 scanf("%d",&w[i]); 17 18 for(i=0; i<n; i++) 19 for(j=m; j>=w[i]; j--) 20 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 21 printf("%d\n",dp[m]); 22 } 23 return 0; 24 }
5. HDU 2159 二维费用背包
问题分析:
dp[i][j] 表示 i 的容量 j 数目下的最大价值。在该题中表示 i 忍耐度下杀 j 个怪物所达到的最大经验值。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int ad[105],su[105],dp[105][105]; 4 int main() 5 { 6 int n,m,k,s,i,j,l; 7 while(scanf("%d%d%d%d",&n,&m,&k,&s)!=EOF) 8 { 9 memset(dp,0,sizeof(dp)); 10 int ans=0; 11 12 for(i=0; i<k; i++) 13 scanf("%d%d",&ad[i],&su[i]); 14 for(i=0; i<k; i++) 15 for(j=1; j<=s; j++) 16 for(l=su[i]; l<=m; l++) 17 { 18 dp[l][j]=max(dp[l][j],dp[l-su[i]][j-1]+ad[i]); 19 if(dp[l][j]>=n) 20 ans=max(ans,m-l); 21 } 22 23 if(dp[m][s]<n) 24 printf("-1\n"); 25 else 26 printf("%d\n",ans); 27 } 28 return 0; 29 }
6. HDU 2955 01背包
问题分析:
dp[i] 表示 i 的容量下 的最大价值。 在该题中表示 在获得 i 的钱数下 能够逃跑的最大概率。
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct P 4 { 5 int money; 6 double opportunity; 7 } p[105]; 8 int main() 9 { 10 int T,n,i,j,sum; 11 double dp[10000]; 12 scanf("%d",&T); 13 while(T--) 14 { 15 double t; 16 sum=0; 17 scanf("%lf%d",&t,&n); 18 for(i=0; i<n; i++) 19 { 20 scanf("%d%lf",&p[i].money,&p[i].opportunity); 21 sum+=p[i].money; 22 } 23 24 for(i=0; i<=sum; i++)dp[i]=0.0; 25 dp[0]=1; 26 27 for(i=0; i<n; i++) 28 for(j=sum; j>=p[i].money; j--) 29 dp[j]=max(dp[j],dp[j-p[i].money]*(1-p[i].opportunity)); 30 31 for(i=sum; i>=0; i--) 32 { 33 if(dp[i]>=1-t) 34 { 35 printf("%d\n",i); 36 break; 37 } 38 } 39 } 40 return 0; 41 }
7. HDU 1712 分组背包
问题: 给你n门课程 m天复习时间
2 2
1 2
1 3 输出:3
两门课程,复习1天 利益值各为 1,复习两天 利益值分别为 2 3。问在出的复习天数能获得最大的利益。
问题分析:
dp[i] 表示在 i 天可以获得的最大利益。
状态方程:dp[j]=max(dp[j],dp[j-k]+s[i][k]); //减去相应的天数,加上相应的价值。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int s[105][105],dp[1005]; 6 int main() 7 { 8 int n,m,i,j,k; 9 while(scanf("%d%d",&n,&m)!=EOF&&n&&m) 10 { 11 memset(dp,0,sizeof(dp)); 12 for(i=1; i<=n; i++) 13 for(j=1; j<=m; j++) 14 scanf("%d",&s[i][j]); 15 for(i=1; i<=n; i++) 16 for(j=m; j>=1; j--) 17 for(k=1; k<=j; k++) 18 dp[j]=max(dp[j],dp[j-k]+s[i][k]); 19 printf("%d\n",dp[m]); 20 } 21 return 0; 22 }
8. HDU 2063 完全背包
问题分析:
在本题中由于股票都是1000的价格,这里全部(总钱数、每种类型的本金数,利息不变)都 除以1000 ,不影响最终结果。
dp[i]表示 i 本金下能够获得的最大利息数 。
1 #include<cstdio> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 int bj[50005],lx[50005],dp[50005]; 6 int main() 7 { 8 int T,money,year,type,i,j,k; 9 scanf("%d",&T); 10 while(T--) 11 { 12 scanf("%d%d%d",&money,&year,&type); 13 for(i=1; i<=type; ++i) 14 { 15 scanf("%d%d",&bj[i],&lx[i]); 16 bj[i]/=1000; 17 } 18 for(i=1; i<=year; i++) 19 { 20 int mm=money/1000; 21 memset(dp,0,sizeof(dp)); 22 for(j=1; j<=type; j++) 23 for(k=bj[j]; k<=mm; k++) 24 dp[k]=max(dp[k],dp[k-bj[j]]+lx[j]); 25 money+=dp[mm]; 26 } 27 printf("%d\n",money); 28 } 29 return 0; 30 }
9. HDU 1171 01背包
问题分析:
要想使的两边的包重量接近相同,需得到全部重量,然后01背包 使其无线接近(对‘每’个价值的机器要么取要么不取)总重量的一半(sum/2,无限接近该值,则该值一定小于等于真实的sum的一半,所以输出sum-dp[sum/2],dp[sum/2])即可。
1 #include<cstdio> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 int num[100005],v[100005],dp[100005]; 6 int main() 7 { 8 int n,i,j,sum,k,tem; 9 while(scanf("%d",&n)!=EOF&&n>=0) 10 { 11 sum=0; 12 k=0; 13 for(i=0; i<n; i++) 14 { 15 scanf("%d%d",&tem,&num[i]); 16 sum+=tem*num[i]; 17 for(j=0; j<num[i]; j++) 18 v[k++]=tem; 19 } 20 memset(dp,0,sizeof(dp)); 21 for(i=0; i<k; i++) 22 for(j=sum/2; j>=v[i]; j--) 23 dp[j]=max(dp[j],dp[j-v[i]]+v[i]); 24 printf("%d %d\n",sum-dp[sum/2],dp[sum/2]); 25 } 26 return 0; 27 }
10. HDU 2639 背包第k优解
问题分析:
在01背包的基础上,
1 #include<cstdio> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 int w[1005],v[1005],dp[10005][35]; 6 int a[50],b[50]; 7 int main() 8 { 9 int T,i,j,k,n,ww,num; 10 scanf("%d",&T); 11 while(T--) 12 { 13 scanf("%d%d%d",&n,&ww,&num); 14 for(i=0; i<n; i++) 15 scanf("%d",&v[i]); 16 for(i=0; i<n; i++) 17 scanf("%d",&w[i]); 18 memset(dp,0,sizeof(dp)); 19 for(i=0; i<n; i++) 20 for(j=ww; j>=w[i]; j--) 21 { 22 for(k=1; k<=num; k++) 23 { 24 a[k]=dp[j][k],b[k]=dp[j-w[i]][k]+v[i]; 25 } 26 int x,y,z; 27 x=y=z=1; 28 a[num+1]=b[num+1]=-1; 29 while(z<=num&&(x<=num||y<=num)) 30 { 31 if(a[x]>b[y]) 32 dp[j][z]=a[x++]; 33 else 34 dp[j][z]=b[y++]; 35 if(dp[j][z]!=dp[j][z-1]) 36 z++; 37 } 38 } 39 printf("%d\n",dp[ww][num]); 40 } 41 return 0; 42 }
11. vijos 1412 第K优解
1 #include<cstdio> 2 using namespace std; 3 int vv[10005],ww[10005],dp[10005][60]; 4 int a[1004],b[1004]; 5 int main() 6 { 7 int k,v,n,i,j,l; 8 while(scanf("%d%d%d",&k,&v,&n)!=EOF) 9 { 10 for(i=0; i<n; i++) 11 scanf("%d%d",&ww[i],&vv[i]); 12 13 for(i=0; i<v; i++) 14 for(j=0; j<k; j++) 15 dp[i][j]=-1e5; 16 dp[0][0]=0; 17 18 for(i=0; i<n; i++) 19 for(j=v; j>=ww[i]; j--) 20 { 21 for(l=0; l<k; l++) 22 { 23 a[l]=dp[j][l]; 24 b[l]=dp[j-ww[i]][l]+vv[i]; 25 } 26 int x=0,y=0,z=0; 27 while(z<k) 28 { 29 if(a[x]>b[y]) 30 dp[j][z]=a[x++]; 31 else 32 dp[j][z]=b[y++]; 33 z++; 34 } 35 } 36 int ans=0; 37 for(i=0; i<k; i++) 38 ans+=dp[v][i]; 39 printf("%d\n",ans); 40 } 41 return 0; 42 }