背包训练

背包问题初始值:

  恰好装满: 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 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 }
View Code

 

  

posted @ 2017-04-12 10:33  马丁黄瓜啊  阅读(294)  评论(0编辑  收藏  举报