码迷,mamicode.com
首页 > 其他好文 > 详细

【动态规划】背包九讲及相应习题

时间:2019-09-07 11:10:34      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:max   csrf   技术   his   while   为什么   video   closed   follow   

【参考博客及视频】

1、大雪菜

2、背包九讲——全篇详细理解与代码实现

3、dd大牛的《背包九讲》

4、背包问题 (附单调队列优化多重背包


 

【题目】

1、Acwing

背包题目

 

2、01背包问题

Luogu 2925 干草出售
Luogu 1616 疯狂的采药
HDU 3466 Proud Merchants

 

3、完全背包问题

HDU 1114 Piggy-Bank
Luogu 1853 投资的最大效益

 

4、多重背包问题

HDU 1059 Dividing
Luogu P1776 宝物筛选

 

5、混合背包问题

Luogu P1833 樱花
HDU 3535 AreYouBusy

 

6、二维费用背包问题

Luogu 1507 NASA的食物计划
HDU 2159 FATE

 

7、分组背包问题

Luogu 1757 通天之分组背包
HDU 1712 ACboy needs your help

 

8、有依赖背包问题

HDU 3449 Consumer
Luogu 1064 金明的预算方案

 

9、泛化物品

HDU 2639 Bone Collector II
HDU 3810 Magina

 


 

 

【小结】

  背包虽说有九讲,其实核心其实只有01背包和完全背包两个需要大家深刻理解后,其余的背包问题只是从不同的角度来提出问题,实质上还是和最基本的背包问题一样。虽然大家觉得九讲很乱很麻烦。一开始我也是这样觉得的,后来看了Acwing上雪菜的讲解,第一感觉是:不仅长得帅还敲得一手好代码,真的不得不佩服人家来自北大。之后可能随着算法基础课的开展,我还要更新一波博客。废话不多说。

 

【01背包】

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

 

 01背包:f[i][j] :前i个物品,其容量为j时的最大价值 

for ( 1~n ):    依次枚举所有物品
       for ( 0 ~ m ):    枚举背包的容量
            if ( j >= v[i] ):    如果当前的物品能放进背包
                f[i][j] = max( f[i-1][j] , f[i-1][j-v[i]]+w[i])
                                    不选        选

 

  

  

 同时这里有一个空间上的优化,主要是基于每次的物品放完后,只有对应的内容进行更新,如果放下一个物品的时候,其实只需要更新上一个状态即可。

01背包,只允许放一次,所以更新的时候还需要  for循环枚举的顺序还需要注意:

 

for( int j = V ; j >= v[i] ; j-- )
  f[j] = max( f[j] , f[j-v[i]] + w[i] )
        该状态 第i个物品还没有被覆盖,
     ∴ f[j-v[i]]实际就是指第i-1个物品时的状态
     ∴ 这里其实 从后往前历遍,每次只询问一回,而且是基于前i-1个物品更新后的值,所以等价于只是放了一遍

 

 

01背包模板:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 const int N = 1e3+5;
 5 
 6 int f[N],v[N],w[N];
 7 int n,m;
 8 
 9 int main()
10 {
11     cin >> n >> m ; 
12     for(int i=0;i<n;i++){
13         cin >> v[i] >> w [i];
14     }
15     for(int i=0;i<n;i++){
16         for(int j=m;j>=v[i];j--){
17             f[j] = max(f[j],f[j-v[i]]+w[i]);
18         }
19     }
20     cout << f[m] << endl; 
21 }

 

观察上面的代码:

1、请问为什么 f[m]就是最终答案?(可能放置的物品容量还没有到达m)

 原因是:与初始化有关,因为初始化为0,例如  在 容量为k的时候就已经把背包的价值达到最大了, f[j] = max( f[j] , f[j-v[i]]+w[i] )

该过程 k+1 从0开始放物品,但其实最后放的物品的种类与f[k]等价的。

  k , k+1 , …… m

∴f[m]此时为答案。

 


 

2、若想把容量恰好为m的最大价值?

  需要对初始化动手脚,初始化f[0]=0,f[其他] = -inf

 


 

完全背包

 

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。

第 i 种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

 

 

f[i]表示总体积为i 时的最大价值

最朴素的做法:基于01背包做法

for( i = 1 ; i <= n ; i++ )  依次枚举所有物品
  for( j = V ; j >= v[i] ; j-- )  枚举当前容量
    for( k = 0 ; k*v[i] <= j ; k++ )  枚举容量下可以放多少个,若能放下的前提下更新
      f[j] = max( f[j] , f[j-k*v[i]]+k*w[i] )  

  

优化后:

for( i = 1 ; i <= n ; i++ )
   for( j = v[i] ; j <= V ; j++ )  //注意枚举的顺序
    f[j] = max( f[j] , f[j-v[i]]+w[i])
  其实在当前位置的j的时候,容量为:j-v[i],可能已经利用该物品进行更新了。
  效果为:多次选择该物品。

 

 

 

  

证明:

数学归纳法:
  1、假设考虑i-1个物品后,所有的f[j]都是正确的
  2、来证明,考虑第i个物品后,所有的f[j]也都是正确的
  对于某个j而言,最优解包含k个v[i]
  f[j-k*v[i]]
  f[j-(k-1)*v[i]]+w[i]
  ……………………
  f[j-v[i]] + w[i]

 

 

完全背包模板

 1 #include<iostream>
 2 using namespace std;
 3 const int N = 1e3+10;
 4 
 5 int f[N],v[N],w[N];
 6 
 7 int n,m;
 8 
 9 int main()
10 {
11     cin >> n >> m ; 
12     for(int i=0;i<n;i++){
13         cin >> v[i] >> w[i] ;
14     }
15     for(int i=0;i<n;i++){
16         for(int j=v[i];j<=m;j++){
17             f[j] = max( f[j] , f[j-v[i]]+w[i]); 
18         }
19     }
20     cout << f[m] << endl;
21     return 0;
22 }

 

 


 

多重背包

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

 

 

朴素做法:

for(int i=1;i<=n;i++)//枚举物品
    for(int j=V;j>=0;j--)//枚举体积
        for(int k=1;k<=num[i],k++)
            //这个枚举到num[i]
            if(j-k*c[i]>=0)//判断能否装下.
                f[j]=max(f[j],f[j-k*c[i]]+k*w[i]);

 

  

二进制优化

二进制拆分的原理

我们可以用 1,2,4,8...2^n 表示出 12^{n+1}-1的所有数.

考虑我们的二进制表示一个数。

根据等比数列求和,我们很容易知道我们得到的数最大就是 2^{n+1}-1

而我们某一个数用二进制来表示的话,每一位上代表的数都是 2 的次幂.

就连奇数也可以,例如-> 19 可以表示为 10011 (2)
?     


二进制拆分的做法

因为我们的二进制表示法可以表示从 1 到 num[i] 的所有数,我们对其进行拆分,就得到好多个大物品(这里的大物品代表多个这样的物品打包得到的一个大物品).

(简单来讲,我们可以用一个大物品代表 1,2,4,8.. 件物品的和。)

而这些大物品又可以根据上面的原理表示出其他不是2的次幂的物品的和.

因此这样的做法是可行的.

我们又得到了多个大物品,所以再去跑01背包即可.

 

  

二进制优化的模板:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 const int N = 2e3+10;
 5 
 6 int f[N],v[N],w[N],s[N];
 7 
 8 int n,m;
 9 
10 int main()
11 {
12     cin >> n >> m; 
13     for(int i=0;i<n;i++){
14         cin >> v[i] >> w[i] >> s[i] ;
15     }
16     for(int i=0;i<n;i++){
17         
18         for(int k=0 ; (1<<k) <= s[i] ; k++ ){
19             int K = (1<<k);
20             for(int j=m ; j>=K*v[i] ; j-- ){
21                 f[j] = max( f[j] , f[j-K*v[i]] + K*w[i] );
22             }
23             s[i] = s[i] - (1<<k);
24         }
25         for(int j=m ; j >= s[i]*v[i] ; j-- ){
26             f[j] = max( f[j] , f[j-s[i]*v[i]] + s[i]*w[i] );
27         }
28     }
29     cout << f[m] << endl ; 
30     return 0 ;
31 }

 

单调队列优化: 

技术图片

例如:物品的容量为4:

容易发现的是,同一颜色的格子,对 c[i] 取模得到的余数相同.

且,它们的差满足等差数列! (公差为 c[i] .

通项公式为 j=k∗c[i]+ 取模得到的余数

所以我们可以根据对 c[i] 取模得到的余数进行分组.

即可分为 0,1,2,3…c[i]−1 共 c[i] 组

且每组之间的状态转移不互相影响.(注意这里是组.相同颜色为一组

相同颜色的格子,位置靠后的格子,将受到位置靠前格子的影响.

即 f[9] 可能受到 f[5] 的影响,也可能受到 f[1] 的影响

而 f[5] 也可能受到 f[1] 的影响.

所以我们考虑将原始状态转移方程变形.

 

设:d = v[i] ; 
  a = j / d ; 
  b = j % d ;

∴ j = a * d + b 
    商  除数 + 余数

原始的式子:
  f[i][j]=max(f[i-1][j],f[j-k*v[i]]+k*w[i]);

转化需要用的式子有:

1、j - k * d = ( a - k ) * d + b
2、a - k = k

推导过程为:
对 j - k * d 进行处理:
  j 利用 偏移量来表示:
  j - k * d 
=  a * d + b - k * d 
= ( a - k ) * d + b 

其中令k = a - k 

a - ( a - k ) = k
a - k = k

转化过程如下:
f[i][j] = max( f[i-1][j-k*d]+k*w[i] )
     = max( f[i-1][k`× d + b] + (a - k`) × w[i] ) 
     = max( f[i-1][k`× d + b] - k` × w[i] ) + a × w[i] (常数可以提到外面)

 

 

单调队列模板

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 const int N = 2e4+10;
 6 typedef struct Node {
 7     int id , val ; 
 8 }node ;
 9 
10 Node Q[N];
11 int n , m , v , w , s ;
12 int f[N];
13 
14 int main()
15 {
16     cin >> n >> m ; 
17     for( int i = 0 ; i < n ; i++ ){
18         cin >> v >> w >> s ;
19         for( int j = 0 ; j < v ; j++ ){  //枚举余数
20             int head = 1 , tail = 0 ;
21             for( int k = j , d = 0 ; k <= m ; k += v , d++ ){  
22                 int r = f[k] - d * w ;
23                 while ( head <= tail && r >= Q[tail].val ) tail -- ;
24                 Q[++tail] = node{ d , r };
25                 while ( Q[head].id < d - s ) head ++ ; 
26                 f[k] = Q[head].val + d * w ;
27             }
28         }
29     }
30     cout << f[m] << endl;
31     return 0;
32 }

 


 

 

混合背包 

有 N 种物品和一个容量是 V 的背包。

物品一共有三类:

第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si 次(多重背包);
每种体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

 

 

输入格式
  第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

  接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

  si=−1 表示第 i 种物品只能用1次;
  si=0 表示第 i 种物品可以用无限次;
  si>0 表示第 i 种物品可以使用 si 次;
输出格式
  输出一个整数,表示最大价值。

数据范围
  0<N,V≤1000
  0<vi,wi≤1000
  −1≤si≤1000

 

 

技术图片
 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 const int N = 1e3+5; 
 6 
 7 int f[N],n,m;
 8 typedef struct Good{
 9     int s,v,w;
10 }Node;
11 
12 Node a[N];
13 int main(){
14     scanf("%d%d",&n,&m);
15     
16     for(int i=0;i<n;i++) scanf("%d%d%d",&a[i].v,&a[i].w,&a[i].s);
17     for(int i=0;i<n;i++){
18         if( a[i].s == -1 ){
19             for(int j=m;j>=a[i].v;j--)
20                 f[j] = max( f[j] , f[j-a[i].v] +a[i].w ); 
21         }else if( a[i].s == 0 ){
22             for(int j=a[i].v;j<=m;j++)
23                 f[j] = max( f[j] , f[j-a[i].v] +a[i].w ); 
24         }else{
25             for(int k=1 ; k <= a[i].s ; k<<=1 ){
26                 a[i].s -= k ;
27                 for(int j=m ; j>=k*a[i].v ; j--)
28                     f[j] = max( f[j] , f[j-k*a[i].v] + k*a[i].w );
29             }
30             if( a[i].s ){
31                 for(int j=m ; j>=a[i].s*a[i].v ; j-- ){
32                     f[j] = max( f[j] , f[j-a[i].s*a[i].v] + a[i].s*a[i].w );
33                 }
34             }
35         }
36     }
37     printf("%d\n",f[m]);
38     return 0 ;
39 }
混合背包

 


 

 

二维费用的背包问题

有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。

每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。

 

  

技术图片
 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 const int N = 1e3+5;
 6 const int M_ = 1e2+5;
 7 
 8 int f[M_][M_];
 9 
10 int n,V,M;
11 int v,m,w;
12 int main()
13 {
14     cin >> n >> V >> M ;
15     
16     for(int i=0;i<n;i++){
17         
18         cin >> v >> m >> w ;
19         
20         for(int j=V ; j>=v ; j-- ){     //体积
21             for(int k=M ; k>=m ; k-- ){     //质量
22                 f[j][k] = max( f[j][k] , f[j-v][k-m] + w ) ; 
23             }
24         }
25     }
26     cout << f[V][M] << endl ; 
27     return 0 ;
28 }
二维费用的背包问题

 


 

 

分组背包问题

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

 

技术图片
 1 #include<iostream>
 2 #include<algorithm>
 3 
 4 using namespace std;
 5 const int N = 1e2 + 5 ; 
 6 
 7 int n,m,s;
 8 int v[N],w[N],f[N];
 9 
10 int main(){
11     cin >> n >> m ;
12     for(int i=0;i<n;i++){
13         
14         cin >> s ;
15         for(int j = 0 ; j < s ; j++ ) cin >> v[j] >> w[j] ;
16         for(int j = m ; j >= 0; j-- ){
17             for(int k = 0 ; k < s ; k++ ){
18                 if( j >= v[k] ){
19                     f[j] = max( f[j] , f[j-v[k]]+w[k] );
20                 }
21             }
22         }
23     }
24     cout << f[m] << endl;
25     return 0;
26 }
分组背包问题

 


 

 

有依赖的背包问题

有 N 个物品和一个容量是 V 的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。

如下图所示:
技术图片 如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。 每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是
1…N。 求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。 输出最大价值。

 

 

技术图片
 1 #include<cstring>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int N = 105;
 7 
 8 int head[N] , To[N] , nxt[N] , idx ;
 9 int f[N][N] , v[N] , w[N] ; 
10 int n , m , p , root ;
11 
12 void Add_edge( int u , int v ){
13     nxt[++idx] = head[u] ;
14     head[u] = idx;
15     To[idx] = v ;
16 }
17 
18 void dfs( int u ){
19     
20     for( int i = head[u] ; ~i ; i = nxt[i] ){
21         int Son = To[i] ;
22         dfs(Son) ;
23         for( int j = m - v[u] ; j >= 0 ; j-- ){
24             for( int k = 0 ; k <= j ; k++ ){
25                 f[u][j] = max( f[u][j] , f[u][j-k] + f[Son][k] );
26             }
27         }    
28     }
29     for( int i = m ; i >= v[u] ; i-- ) f[u][i] = f[u][i-v[u]] + w[u] ;
30     for( int i = 0 ; i < v[u] ; i++ ) f[u][i] = 0;
31 }
32 int main()
33 {
34     memset( head , -1 , sizeof head );
35     
36     cin >> n >> m ;
37     for( int i = 1 ; i <= n ; i++ ){
38         cin >> v[i] >> w[i] >> p ;
39         if( p == -1 ) root = i ; 
40         else{
41             Add_edge( p , i );
42         }
43     }
44     dfs(root);
45     printf("%d\n",f[root][m]);
46     return 0 ;
47 }
有依赖背包问题

 

 


 

 

背包问题求方案数

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 最优选法的方案数。注意答案可能很大,请输出答案模 1e9+7 的结果。

  

技术图片
 1 #include<cstring>
 2 #include<iostream>
 3 #include<algorithm>
 4 
 5 using namespace std;
 6 const int N = 1e3+5 ;
 7 const int Inf = 2e6 ;
 8 const int Mod = 1e9+7 ;
 9 
10 int n,m,w,v;
11 
12 int f[N] ;
13 int Way[N] ;
14 
15 int main()
16 {
17     cin >> n >> m ;
18     
19     //Initization
20     Way[0] = 1 ; f[0] = 0 ;
21     for(int i = 1 ; i <= m ; i++ ){
22         f[i] = -Inf ;
23     }
24     
25     for( int i = 0 ; i<n ; i++ ){
26         cin >> v >> w ;
27         for( int j = m ; j >= v ; j-- ){
28             int tmp =  f[j] , Path = 0 ;
29             f[j] = max( f[j] , f[j-v] + w );
30             if( f[j] == tmp )  Path += Way[j] ; 
31             if( f[j] == f[j-v] + w ) Path += Way[j-v];
32             if( Path >= Mod )   Path -= Mod ;
33             Way[j] = Path ; 
34         }
35     }
36     
37     int Maxz = -1 , ans = 0 ; 
38     for( int i = 1  ; i <= m ; i++ )
39         Maxz = max( Maxz , f[i] );
40     for( int i = 0 ; i <= m ;i++ ){
41         if( Maxz == f[i] ){
42             ans = ans + Way[i] ; 
43             if( ans >= Mod )
44                 ans -= Mod ;
45         }
46     }
47 
48     cout << ans << endl ;
49     return 0 ; 
50 }
背包问题求方案数

 

 


 

 

背包问题求具体方案

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1…N。

 

技术图片
 1 #include<iostream>
 2 #include<algorithm>
 3 
 4 using namespace std;
 5 const int N = 1010;
 6 
 7 int v[N],w[N];
 8 int f[N][N];
 9 int n,m;
10 
11 int main()
12 {
13     cin >> n >> m ;
14     
15     for( int i = 1 ; i<=n ; i++ )
16         cin >> v[i] >> w[i] ;
17     
18     for( int i = n ; i>=1 ; i-- ){
19         for( int j = 0 ; j <= m ;j++ ){
20             f[i][j] = f[ i+1 ][j] ; 
21             if( j >= v[i] ) f[i][j] = max( f[i][j] , f[i+1][j-v[i]] + w[i] );
22         }
23     }
24     
25     int vol = m ;
26     for(int i=1;i<=n && vol > 0 ;i++){
27         if( vol >= v[i] && f[i][vol] == f[ i+1 ][ vol-v[i] ] + w[i] ){
28             cout << i << " " ;
29             vol -= v[i] ; 
30         }
31     }
32     return 0 ;
33 }
背包问题求具体方案

 


 

 

 

 

2、01背包问题

 

Luogu 2925 干草出售

技术图片
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N = 5e4+10;
 6 int f[N];
 7 int main()
 8 {
 9     int V,n;
10     scanf("%d%d",&V,&n);
11     for(int i=1,v;i<=n;i++){
12         scanf("%d",&v);
13         for(int j=V;j>=v;j--){
14             f[j] = max( f[j-v]+v , f[j] );
15         }
16     }
17     printf("%d\n",f[V]);
18     return 0;
19 }
干草出售

 

Luogu 1616 疯狂的采药

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e5+10;
 4 int f[N];
 5 int n,v,w,V;
 6 int main()
 7 {
 8     ios_base :: sync_with_stdio(false);
 9     cin.tie(NULL) , cout.tie(NULL) ;
10     cin >> V >> n ;
11     for( int i = 1 ; i <= n ; i++ ){
12         cin >> v >> w ;
13         for( int j = v ; j <= V ; j ++ ){
14             f[j] = max( f[j-v]+w , f[j] );
15         }
16     }
17     cout << f[V] << endl;
18     return 0;
19 }
疯狂的采药

 

HDU 3466 Proud Merchants

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 5e3+10;
 4 typedef struct Node{
 5     int v , p , val ;
 6 }Node ;
 7 Node a[N];
 8 
 9 bool cmp ( Node u , Node v ){
10     return u.p - u.v < v.p - v.v ;
11 }
12 
13 int f[N];
14 int n , V ;
15 int main(){
16 
17     while( scanf("%d%d",&n,&V)!=EOF ){
18         for( int i = 0 ; i <= V ; i++ ) f[i] = 0 ;
19         for( int i = 0 ; i < n ; i++ ){
20             scanf("%d%d%d",&a[i].v , & a[i].p , & a[i].val );
21         }
22         sort( a , a + n , cmp );
23         for( int i = 0 ; i < n ; i++ ){
24             for( int j = V ; j >= a[i].p && j >= a[i].v ; j-- ){
25                 f[j] = max( f[j] , f[j-a[i].v] + a[i].val );
26             }
27         }
28         printf("%d\n",f[V]);
29     }
30     return 0 ;
31 }
Proud Merchants

 

 

 

3、完全背包问题

 

HDU 1114 Piggy-Bank

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int inf = 0x3f3f3f3f;
 4 const int N = 1e4+10;
 5 int f[N],n;
 6 
 7 int main()
 8 {
 9     int T,E,F,V;
10     scanf("%d",&T);
11     while(T--){
12         scanf("%d%d",&E,&F);
13         V = F - E ;
14 
15         for( int i = 1 ; i <= V ; i++ ) f[i] = inf ;
16 
17         scanf("%d",&n);
18         for( int i=0,v,w;i<n;i++){
19             scanf("%d%d",&w,&v);
20             for(int j = v ; j <= V; j++ ){
21                 f[j] = min( f[j], f[j-v]+w );
22             }
23         }
24 
25         if( f[V] == inf ){
26             printf("This is impossible.\n");
27         }else{
28             printf("The minimum amount of money in the piggy-bank is %d.\n",f[V]);
29         }
30 
31     }
32     return 0;
33 }
Piggy-Bank

 

Luogu 1853 投资的最大效益

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e7+10 ;
 4 int f[N],w[N],v[N];
 5 int S , n , D , V;
 6 int main()
 7 {
 8 
 9     cin >> V >> n >> D ;
10     for( int i = 0 ; i < D ; i++ ){
11         cin >> v[i] >> w[i] ;
12     }
13     //cout << "###" << endl;
14     for( int i = 0 ; i < n ; i++ ){
15         for( int d = 0 ; d < D ; d++ ){
16             for( int j = v[d] ; j <= V  ; j++ ){
17                 f[j] = max( f[j] , f[j-v[d]] + w[d] );
18                 //cout << f[j] << endl;
19             }
20         }
21         V = V + f[V];
22     }
23     cout << V + f[V]<< endl;
24     return 0;
25 }
投资的最大效益

 

 

 

4、多重背包问题

 

HDU 1059 Dividing

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 120050  ;
 4 int f[N];
 5 int a[7];
 6 int main()
 7 {
 8     int kase = 0 ;
 9     while( 1 ){
10 
11         int sum = 0 ;
12         for( int i = 1 ; i <= 6 ; i++ ){
13             scanf("%d",&a[i]);
14             //a[i] *= i ;
15             sum += i*a[i] ;
16         }
17 
18         if( sum == 0 ) break ;
19         //if( kase ) puts("");
20         if( sum & 1 ){
21             printf("Collection #%d:\nCan‘t be divided.\n\n",++kase);
22             continue;
23         }
24 
25         for( int i = 1 ; i <= sum/2 ; i++ ) f[i] = 0 ;
26 
27         for( int i = 1 ; i <= 6 ; i++ ){
28             for( int k = 0 ; 1 << k <= a[i] ; k++ ){
29                 int K = 1 << k ;
30                 a[i] -= K ;
31                 for( int j = sum/2 ; j >= K * i ; j -- ){
32                     f[j] = max( f[j] , f[j-K*i] + K*i );
33                 }
34             }
35             for( int j = sum/2 ; j >= a[i]*i ; j -- ){
36                 f[j] = max( f[j] , f[j-a[i]*i] + a[i]*i );
37             }
38         }
39         /*
40         puts(" ##### ");
41         for( int i = 0 ; i <= sum/2 ;i++ ){
42             printf("%d%c",f[i],i==sum/2?‘\n‘:‘ ‘);
43         }
44         */
45         if( f[sum/2] == sum/2) {
46             printf("Collection #%d:\nCan be divided.\n\n",++kase);
47         }else{
48             printf("Collection #%d:\nCan‘t be divided.\n\n",++kase);
49         }
50     }
51     return 0 ;
52 }
Dividing


Luogu P1776 宝物筛选

技术图片
 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 const int N = 2e5+10;
 6 typedef struct Node {
 7     int id , val ;
 8 }node ;
 9 
10 Node Q[N];
11 int n , m , v , w , s ;
12 int f[N];
13 
14 int main()
15 {
16     cin >> n >> m ;
17     for( int i = 0 ; i < n ; i++ ){
18         cin >> w >> v >> s ;
19         for( int j = 0 ; j < v ; j++ ){
20             int head = 1 , tail = 0 ;
21             for( int k = j , d = 0 ; k <= m ; k += v , d++ ){
22                 int r = f[k] - d * w ;
23                 while ( head <= tail && r >= Q[tail].val ) tail -- ;
24                 Q[++tail] = node{ d , r };
25                 while ( Q[head].id < d - s ) head ++ ;
26                 f[k] = Q[head].val + d * w ;
27             }
28         }
29     }
30     cout << f[m] << endl;
31     return 0;
32 }
宝物筛选

 

 

5、混合背包问题

 

Luogu P1833 樱花

技术图片
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int N = 1e5+5;
 7 
 8 int f[N],n,m;
 9 typedef struct Good{
10     int s,v,w;
11 }Node;
12 
13 Node a[N];
14 int main(){
15     int T1,T2,M1,M2;
16     scanf("%d:%d %d:%d %d",&T1,&M1,&T2,&M2,&n);
17 
18     m = T2 * 60 + M2 - ( T1 * 60 + M1 );
19 
20     for(int i=0;i<n;i++) scanf("%d%d%d",&a[i].v,&a[i].w,&a[i].s);
21     for(int i=0;i<n;i++){
22         if( a[i].s == 1 ){
23             for(int j=m;j>=a[i].v;j--)
24                 f[j] = max( f[j] , f[j-a[i].v] +a[i].w );
25         }else if( a[i].s == 0 ){
26             for(int j=a[i].v;j<=m;j++)
27                 f[j] = max( f[j] , f[j-a[i].v] +a[i].w );
28         }else{
29             for(int k=1 ; k <= a[i].s ; k<<=1 ){
30                 a[i].s -= k ;
31                 for(int j=m ; j>=k*a[i].v ; j--)
32                     f[j] = max( f[j] , f[j-k*a[i].v] + k*a[i].w );
33             }
34             if( a[i].s ){
35                 for(int j=m ; j>=a[i].s*a[i].v ; j-- ){
36                     f[j] = max( f[j] , f[j-a[i].s*a[i].v] + a[i].s*a[i].w );
37                 }
38             }
39         }
40     }
41     printf("%d\n",f[m]);
42     return 0 ;
43 }
樱花

 

HDU 3535 AreYouBusy

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 120;
 4 int f[N][N];
 5 int n,m,V,s;
 6 int main()
 7 {
 8     while( ~scanf("%d%d",&n,&V) ){
 9         memset( f , 0 , sizeof(f) );
10         for( int i = 1 ,v , w ; i <= n ; i++ ){
11             scanf("%d%d",&m,&s);
12 
13             if( s == 0 ){
14                 for( int p = 0 ; p <= V ; p++ )
15                     f[i][p] = -1 ;
16             }else{
17                 for( int p = 0 ; p <= V ; p++ )
18                     f[i][p] = f[i-1][p] ;
19             }
20 
21             while(m--){
22                 scanf("%d%d",&v,&w);
23                 for( int j = V ; j >= v ; j-- ){
24                     if( s == 0 ){       //至少选一个
25                         if( f[i][j-v] != -1 )
26                             f[i][j] = max( f[i][j] , f[i][j-v] + w );
27                         if( f[i-1][j-v] != -1 )
28                             f[i][j] = max( f[i][j] , f[i-1][j-v] + w );
29                     }else if( s == 1 ){ //最多选一个
30                         if( f[i-1][j] != -1 )
31                             f[i][j] = max( f[i][j] , f[i-1][j] );
32                         if( f[i-1][j-v] != -1 )
33                             f[i][j] = max( f[i][j] , f[i-1][j-v] + w );
34                     }else{              //随机
35 
36                         if( f[i][j-v] != -1 )
37                             f[i][j] = max( f[i][j] , f[i][j-v] + w ) ;
38                         if( f[i-1][j-v] != -1 )
39                             f[i][j] = max( f[i][j] , f[i-1][j-v] + w );
40 
41                         //f[i][j] = max( f[i][j] , f[i-1][j-v] + w );
42                     }
43                 }
44             }
45         }
46         printf("%d\n",f[n][V]);
47     }
48     return 0 ;
49 }
AreYouBusy

 

 

6、二维费用背包问题

 

Luogu 1507 NASA的食物计划

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 450;
 4 int f[N][N];
 5 int n,M,V;  // Number , Weight , Volume
 6 int v,m,c;  // volume , weight , Calorie
 7 
 8 int main()
 9 {
10     cin >> V >> M ;
11     cin >> n ;
12     for( int i = 1 ; i <= n ; i++ ){
13         cin >> v >> m >> c ;
14         for( int j = V ; j >=v ; j-- ){
15             for ( int k = M ; k >= m ; k-- ){
16                 f[j][k] = max( f[j][k] , f[j-v][k-m] + c );
17             }
18         }
19     }
20     cout << f[V][M] << endl;
21     return 0;
22 }
23 
24 
25 /*
26 320 350
27 4
28 160 40 120
29 80 110 240
30 220 70 310
31 40 400 220
32 */
NASA的食物计划

 

HDU 2159 FATE 

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e2+10 ;
 4 int target , V , n , M ;
 5 int f[N] ;
 6 int g[N] ;
 7 int main()
 8 {
 9     while( ~scanf("%d%d%d%d",&target , &V , &n, &M ) ){
10         memset( f , 128 , sizeof f );
11         memset( g , 0 , sizeof g );
12         f[0] = 0 ;
13         int v , w ;
14         while(n--){
15             scanf("%d%d",&w,&v);
16             for(int j = v ; j <= V && g[j-v] + 1 <= M ; j++ ){
17                 if( f[j] < f[j-v] + w ){
18                     f[j] = f[j-v] + w ;
19                     g[j] = g[j-v] + 1 ;
20                 }
21             }
22         }
23         int ans = -1 ;
24         for( int i = 1 ; i <= V ; i++ ){
25             if( f[i] >= target ){
26                 ans = V - i ;
27                 break;
28             }
29         }
30         printf("%d\n",ans);
31     }
32     return 0;
33 }
FATE

 

 

 

7、分组背包问题

 

Luogu 1757 通天之分组背包

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e3 + 5 ;
 4 
 5 int n,V ;
 6 int f[N];
 7 
 8 typedef struct Node{
 9     int v , w ;
10     Node ( int V = 0 , int W = 0 ):v(V),w(W){}
11 }Good;
12 
13 
14 vector <Node> Kind[N];
15 
16 int main(){
17 
18     ios_base :: sync_with_stdio(false);
19     cin.tie(NULL) , cout.tie(NULL) ;
20 
21     cin >> V >> n ;
22     for( int i = 1 , v , w , No ; i <= n ; i++ ){
23         cin >> v >> w >> No ;
24         Kind[No].push_back( Node(v,w) );
25     }
26 
27     for(int i=0;i<N;i++){
28         if( Kind[i].empty() ) continue ;
29         for( int j = V ; j >= 0 ; j -- ){
30             for( auto k : Kind[i] ){
31                 if( j >= k.v ){
32                     f[j] = max( f[j] , f[ j - k.v ] + k.w );
33                 }
34             }
35         }
36     }
37 
38     cout << f[V] << endl;
39 
40     return 0;
41 }
42 
43 /*
44     cin >> n >> m ;
45     for(int i=0;i<n;i++){
46 
47         cin >> s ;
48         for(int j = 0 ; j < s ; j++ ) cin >> v[j] >> w[j] ;
49         for(int j = m ; j >= 0; j-- ){
50             for(int k = 0 ; k < s ; k++ ){
51                 if( j >= v[k] ){
52                     f[j] = max( f[j] , f[j-v[k]]+w[k] );
53                 }
54             }
55         }
56     }
57     cout << f[m] << endl;
58 */
通天之分组背包

 

HDU 1712 ACboy needs your help

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e2 + 20 ;
 4 int G[N][N] ;
 5 int f[N];
 6 int n,m;
 7 int main()
 8 {
 9     while( scanf("%d%d",&n,&m) ,(n+m) ){
10 
11         memset( f , 0 , sizeof f );
12 
13         for( int i = 1 ; i <= n ; i++ ){
14             for( int j = 1 ; j <= m ;j++ ){
15                 scanf("%d",&G[i][j]);
16             }
17         }
18 
19         for( int i = 1 ; i <= n ; i++ ){
20             for( int j = m ; j >= 1 ; j-- ){
21                 for( int k = 1 ; k <= m ; k++ ){
22                     if( j >= k ){
23                         f[j] = max( f[j] , f[j-k] + G[i][k] );
24                     }
25                 }
26             }
27         }
28 
29         printf("%d\n",f[m]);
30     }
31     return 0 ;
32 }
ACboy

 

 

8、有依赖背包问题

 

HDU 3449 Consumer

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N = 1e5 + 10 ;
 5 
 6 typedef struct Node{
 7     int v , w , Fa , Kind ;
 8 }Node ;
 9 Node a[N];
10 
11 ll f[N],g[N];
12 int n ,m, V , idx = 0 ;
13 
14 int main()
15 {
16 
17     while( scanf("%d%d",&n,&V) != EOF){
18 
19         for( int i = 0 ; i <= V ; i++ ) f[i] = 0 ;
20         idx = 0 ;
21 
22         for( int i = 1 ; i <= n ; i++ ){
23             scanf("%d%d",&a[idx].v , &m );
24             a[idx].w = a[idx].Fa = 0 ;
25             a[idx].Kind = i ;
26             idx ++ ;
27             for( int j = 0 ; j < m ; j++ ){
28                 scanf("%d%d",&a[idx].v , &a[idx].w);
29                 a[idx].Kind = a[idx].Fa = i ;
30                 idx ++ ;
31             }
32         }
33 
34         for( int i = 0 ; i < idx ;i++){
35             if( !a[i].Fa ){
36                 for( int j = 0 ; j < a[i].v ; j++ )
37                     g[j] = 0 ;
38 
39                 for( int j = a[i].v ; j <= V ; j++ )
40                     g[j] = f[j-a[i].v]+a[i].w;
41 
42                 for( int k = 0 ; k < idx ; k++ ){
43                     if( a[k].Fa == a[i].Kind ){
44                         for( int j = V ; j >= a[k].v + a[i].v ; j -- ){
45                             g[j] = max( g[j] , g[j-a[k].v] + a[k].w );
46                         }
47                     }
48                 }
49                 for( int j = a[i].v ; j <= V ; j++ )
50                     f[j] = max( f[j] , g[j] );
51 
52             }
53         }
54         printf("%lld\n",f[V]);
55     }
56     return 0;
57 }
Consumer

 


Luogu 1064 金明的预算方案

技术图片
 1 #pragma GCC optimize(2)
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 const int M = 4e4+10;
 5 const int N = 65 ;
 6 
 7 /* Add_edge */
 8 typedef struct Node{
 9     int to , next ;
10 }Edge ;
11 
12 Edge e[N<<1];
13 int head[N] , idx ;
14 void Add_edge(int u,int v){
15     e[idx] = Edge{ v , head[u] } ;
16     head[u] = idx++ ;
17 }
18 void Init(){
19     idx = 0;
20     memset( head , -1 , sizeof head );
21 }
22 
23 /* ___If you ask me how much i love you ______*/
24 /*    The moonlight stands for my heart       */
25 
26 
27 /*              Package             */
28 
29 int n , V ;
30 int v[N] , w[N] ;
31 int f[N][M];
32 void dfs( int u ){
33     for( int i = head[u] ; ~i ; i = e[i].next ){
34         int Son = e[i].to ;
35         dfs( Son ) ;
36         for( int j = V - v[u] ; j >= 0 ; j -- ){
37             for( int k = 0 ; k <= j ; k ++ ){
38                 f[u][j] = max( f[u][j] , f[u][j-k] + f[Son][k] );
39             }
40         }
41     }
42     for( int i = V ; i >= v[u] ; i -- ) f[u][i] = f[u][i-v[u]] + v[u] * w[u] ;
43     for( int i = 0 ; i < v[u] ; i++ ) f[u][i] = 0 ;
44 }
45 
46 /*                                  */
47 
48 int main()
49 {
50     Init() ;
51     ios_base :: sync_with_stdio(false);
52     cin.tie(NULL) , cout.tie(NULL);
53     cin >> V >> n ;
54     for( int i = 1 , Fa ; i <= n ; i++ ){
55         cin >> v[i] >> w[i] >> Fa ;
56         Add_edge( Fa , i );
57     }
58     dfs( 0 ) ;
59     cout << f[0][V] << endl;
60     return 0 ;
61 }
金明的预算
 
技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 5e6+5;
 4 const int M = 1e3+5;
 5 int h[N];
 6 int f[N];
 7 typedef struct Node{
 8     int v , w , Fa;
 9 }Node ;
10 Node a[M];
11 
12 int V , n ;
13 int main(){
14     ios_base :: sync_with_stdio(false);
15     cin.tie(NULL) , cout.tie(NULL) ;
16     cin >> V >> n ;
17 
18     for( int i = 1 ; i <= n ; i++ ){
19         cin >> a[i].v >> a[i].w >> a[i].Fa ;
20         a[i].w *= a[i].v  ;
21     }
22     for( int i = 1 ; i <= n ; i++ ){
23         if( !a[i].Fa ){
24             for( int j = 0 ; j <= a[i].v ; j++ ) h[i] = 0 ;
25             for( int j = a[i].v ; j <= V  ; j++ )
26                 h[j] = f[j-a[i].v]+a[i].w ;
27 
28             for( int j = 1 ; j <= n ; j ++ ){
29                 if( a[j].Fa == i ){
30                     for( int k = V ; k >= a[i].v + a[j].v ; k-- ){
31                         h[k] = max( h[k] , h[k-a[j].v] + a[j].w );
32                     }
33                 }
34             }
35             for( int j = a[i].v ; j <= V ; j++ )
36                 f[j] = max( f[j] , h[j] );
37 
38         }
39     }
40     cout << f[V] << endl ;
41     return 0 ;
42 }
金明的预算

 

 

9、泛化物品

 

HDU 2639 Bone Collector II

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e3+10;
 4 const int M = 65;
 5 int f[N][M];
 6 int a[N],b[N];
 7 int v[N],w[N];
 8 int n,V,k;
 9 int main(){
10     int T ;
11     scanf("%d",&T);
12     while(T--){
13         memset(f,0,sizeof f);
14         scanf("%d%d%d",&n,&V,&k);
15         for( int i = 0 ; i < n ; i++ )
16             scanf("%d",&w[i]);
17         for( int i = 0 ; i < n ; i++ )
18             scanf("%d",&v[i]);
19 
20         for( int i = 0 ; i < n ; i++ ){
21             for( int j = V ; j >= v[i] ; j-- ){
22                 for( int t = 1 ; t <= k ; t ++ ){
23                     a[t] = f[j][t] ;
24                     b[t] = f[j-v[i]][t] + w[i] ;
25                 }
26 
27                 int m , x , y ;
28                 m = x = y = 1 ;
29                 a[k+1] = b[k+1] = -1 ;
30                 while( m <= k && (a[x] != -1 || b[y] != -1 ) ){
31                     if( a[x] > b[y] ){
32                         f[j][m] = a[x++] ;
33                     }else{
34                         f[j][m] = b[y++] ;
35                     }
36                     if( f[j][m] != f[j][m-1] )
37                         m++ ;
38                 }
39             }
40         }
41         printf("%d\n",f[V][k]);
42     }
43     return 0 ;
44 }
第k大背包

 

HDU 3810 Magina

 

【动态规划】背包九讲及相应习题

标签:max   csrf   技术   his   while   为什么   video   closed   follow   

原文地址:https://www.cnblogs.com/Osea/p/11470912.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!