背包DP整理

Posted kongbursi-2292702937

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背包DP整理相关的知识,希望对你有一定的参考价值。

01背包例题:

题目:传送门

 

/*给你n种不同的物品,每个物品有自己的体积w[i]和价值v[i],如果每个物品只能拿一次,给你容量为m的背包,怎样才能获取最大价值;
主线:使容量为m的背包装入的物品价值最大;
dp[j] 记录当容量为j时的可行取法的最大价值
状态转移方程:dp[j]=max(  dp[j],  dp[j-w[i]] +v[i]]  ); 
*/

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e3+5;
 4 int w[maxn],v[maxn],dp[maxn];
 5 int main()
 6 {
 7     int t;
 8     scanf("%d",&t);
 9     while(t--)
10     {
11         int n,m;
12         scanf("%d%d",&n,&m);
13         for(int i=0;i<n;i++)
14             scanf("%d",&v[i]);
15         for(int i=0;i<n;i++)
16             scanf("%d",&w[i]);
17         memset(dp,0,sizeof(dp));
18         for(int i=0;i<n;i++)  
19         {
20             for(int j=m;j>=w[i];j--) //因为每一个物品只能使用一次,所以为了防止dp[j-w[i]]中也使用了第i个物品,我们就应该倒序循环 
21                 dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
22         }
23         printf("%d
",dp[m]);
24     }
25 }
View Code

 

完全背包:

 题目:传送门

 

刚开始m钱 一共years年 d多个基金,第i个股票需要 花费cost[i],收益w[i]。问最大能收益多少?
每一年是一个多重背包问题,因为每一个股票可以购买多次。。那么一共years个背包问题。
卡就卡在开多大数组。因为开不了那么大的数组。。所以,因为基金都是1000的倍数,所以把m和基金的花费都缩小1000去进行背包求解,
得到的解用未缩小的总钱数去加,然后再缩小1000去求解下一年的。

技术图片
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #define MAX 1000100
 5 using namespace std;
 6 long long dp[MAX];
 7 int v[1000];
 8 int w[1000];
 9 int main()
10 {
11    long long  n,d,i,j,money,year,mon,ww,vv;
12    scanf("%lld",&n);
13    while(n--)
14    {
15        memset(v,0,sizeof(v));
16        memset(w,0,sizeof(w));
17        scanf("%lld%lld",&money,&year);
18        scanf("%lld",&d);
19        for(i=1;i<=d;i++)
20        {
21            scanf("%lld%lld",&ww,&v[i]);
22            w[i]=ww/1000;
23        }
24         memset(dp,0,sizeof(dp));
25        while(year--)  //因为每一年你所拥有的钱都不一样,所以最优解也会改变 
26        {
27            mon=money/1000;
28            for(i=1;i<=d;i++) //d种股票 
29             for(j=w[i];j<=mon;j++) //因为每种股票可以使用多次,所以我们就要正着来循环,因为这样的话我们用到的dp[j-w[i]]肯定已经 
30               dp[j]=max(dp[j],dp[j-w[i]]+v[i]);     //找到了最优解,这个最优解可能使用过了第i个股票,所以就满足题意 
31            money+=dp[j-1];  //最后一个就是答案 
32        }
33        printf("%lld
",money);
34    }
35     return 0;
36 }
View Code

 

多重背包:

题目:传送门

 

Input
输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。
Output
对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。
Sample Input
1
8 2
2 100 4
4 100 2
Sample Output
400

题解:

使用多重背包解决物品有限问题

每一种物品都有个数,这个时候我们可以首先把他们用二进制分开,然后再用01背包方法找最优解。

这里解释一下怎么把他们用二进制分开:比如一个物品有10个,那么就可以把这个物品分成1、2、4、8,分别把这一种物品得1个在一起,2个在一起,4个在一起......当成一个新的物品,而且每个物品只有一个,最后剩下的也为一个物品。让它转化成这样后就可以使用01背包来做

 

为什么可以这样做:因为你的最优解中每一个物品用的次数都可以用1、2、4、8....来组成,他们可以组成1—n中所有的数

 

注释+二进制优化代码:

技术图片
 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 class node{
 6 public:
 7     int p, h, c;
 8 }rice[105];
 9 int n, m;
10 int dp[105];
11 
12 int main(){
13     
14     int C;
15     cin >> C;
16     while( C-- ){
17         memset( dp, 0, sizeof( dp ) );
18 
19         cin >> n >> m;
20 
21         for( int i = 1; i <= m; i++ )
22             cin >> rice[i].p >> rice[i].h >> rice[i].c;
23 
24         for( int  i = 1; i <= m; i++ ){  //枚举物品 
25             int p = 1;
26             while( p < rice[i].c ){   //把物品二进制分开 
27                 for( int j = n; j >= rice[i].p * p; j-- )
28                     if( dp[j] < dp[j - rice[i].p * p] + rice[i].h * p)
29                         dp[j] = dp[j - rice[i].p * p] + rice[i].h * p;
30 
31                 rice[i].c -= p;
32                 p <<= 1;  //相当于乘与2 
33             }
34 
35             for( int j = n; j >= rice[i].p * rice[i].c; j-- ) //01背包格式 
36                 if( dp[j] < dp[j - rice[i].p * rice[i].c] + rice[i].h * rice[i].c )
37                     dp[j] = dp[j - rice[i].p * rice[i].c] + rice[i].h * rice[i].c;
38         }
39 
40         cout << dp[n] << endl;
41     }
42     return 0;
43 }
View Code

 没有二进制代码:

技术图片
 1 #include<stdio.h>
 2 #include<string.h>
 3 int max(int x,int y)
 4 {
 5     if(x>y) return x;
 6     else return y;
 7 }
 8 int q[1105],v[105],w[105],e[105];
 9 int main()
10 {
11     int a,s,d,f,g,h;
12     scanf("%d",&a);
13     while(a--)
14     {
15         memset(v,0,sizeof(v));
16         scanf("%d%d",&s,&d);
17         for(f=1;f<=d;++f)
18         {
19             scanf("%d%d%d",&q[f],&w[f],&e[f]);
20         }
21         for(f=1;f<=d;++f) //枚举物品 ,完全暴力dp 
22         {
23             for(g=s;g>=q[f];--g)   
24             {
25                 for(h=1;h<=e[f];++h)
26                 {
27                     if(h*q[f]>g) break;
28                     else
29                     {
30                         v[g]=max(v[g],v[g-h*q[f]]+h*w[f]);
31                     }
32                 }
33             }
34         }
35         printf("%d
",v[s]);
36     }
37     return 0;
38 }
View Code

 

三种背包的混合:

 

就只需要判断一下是完全背包还是多重背包就可以了,加一个判断

代码:

技术图片
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 int m,n,w[31],c[31],p[31],f[201];
 7 int main()
 8 {
 9     scanf("%d%d",&m,&n);
10     for(int i=1;i<=n;i++)
11      scanf("%d%d%d",&w[i],&c[i],&p[i]);
12     for(int i=1;i<=n;i++)
13      if(p[i]==0) //完全背包 
14      {
15          for(int j=w[i];j<=m;j++)
16           f[j]=max(f[j],f[j-w[i]]+c[i]);
17      }
18      else //多重背包 
19      {
20          for(int j=1;j<p[i];j++)  //这一层for循环还可以用二进制来优化 
21           for(int k=m;k>=w[i];k--)
22            f[k]=max(f[k],f[k-w[i]]+c[i]);
23      }
24      printf("%d",f[m]);
25      return 0;
26 }
View Code

 

二维费用背包:

例题:(ssl 2290)潜水员

潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?
例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:
    3 36 120
    10 25 129
    5 50 250
    1 45 130
    4 20 119
如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。
【输入格式】
第一行有2整数m,n(1<=m<=21,1<=n<=79)。它们表示氧,氮各自需要的量。
第二行为整数k(1<=n<=1000)表示气缸的个数。
此后的k行,每行包括ai,bi,ci(1<=ai<=21,1<=bi<=79,1<=ci<=800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。
【输出格式】
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。
 
代码:
技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 const int MAXN=1001;
 5 const int MAXM=101;
 6 const int INF=0x3f3f3f3f;
 7 using namespace std;
 8 int v,u,k;
 9 int a[MAXN],b[MAXN],c[MAXN];
10 int f[MAXM][MAXM];
11 int main()
12 {
13     memset(f,INF,sizeof(f));
14     printf("%d
",f[1][1]); 
15     f[0][0]=0;
16     scanf("%d%d%d",&v,&u,&k);
17     for(int i=1;i<=k;i++)
18      scanf("%d%d%d",&a[i],&b[i],&c[i]);
19     for(int i=1;i<=k;i++)  //枚举罐子 
20      for(int j=v;j>=0;j--)   //枚举氧气容量 
21       for(int l=u;l>=0;l--)  //枚举氮气容量 
22       { 
23           int t1=j+a[i],t2=l+b[i];
24           if(t1>v) t1=v;
25           if(t2>u) t2=u;
26           if(f[t1][t2]>f[j][l]+c[i]) f[t1][t2]=f[j][l]+c[i];
27       }
28       printf("%d",f[v][u]);
29       return 0;
30 }
View Code

 

 

以上是关于背包DP整理的主要内容,如果未能解决你的问题,请参考以下文章

[计数dp] 整数划分(模板题+计数dp+完全背包变种题)

夏令营讲课内容整理 Day 5.

动态规划题目整理

背包dp总结

dp背包问题

被遗忘的DP -01背包代码