背包问题

1. 01背包问题

二维表示

对于01背包一维优化的一点理解:
二维转化为一维:
删掉了第一维:在前i个物品中取。
f[j]表示:拿了总体积不超过j的物品,最大总价值。

为何能转化为一维?
二维时的更新方式:f[i][j]=max(f[i - 1][j] ,f[i - 1][j - v[i]] + w[i]);
1.我们发现,对于每次循环的下一组i,只会用到i-1来更新当前值,不会用到i-2及之前值。于是可以在这次更新的时候,将原来的更新掉,反正以后也用不到。
所以对于i的更新,只需用一个数组,直接覆盖就行了。
2.我们发现,对于每次j的更新,只需用到之前i-1时的j或者j-v[i],不会用到后面的值。
所以为了防止串着改,我们采取从后往前更新的方式,用原来i-1的数组来更新i。
(如果从前往后更新的话,前面的更新过之后,会接着更新后面的值,这样就不能保证是用原来i-1的数组来更新i的了)

如何转化为一维呢?
只用一个数组,每次都覆盖前面的数组。

1.如果当前位置的东西不拿的话,和前一位置的信息(原来i-1数组的这个位置上的值)是相同的,所以不用改变。
2.如果当前位置的东西拿了的话,需要和前一位置的信息(原来i-1数组的这个位置上值)取max。
所以,更新方式就为:f[j]=max(f[j],f[j-v[i]]+w[i]);

整个更新方式就相当于:

每次i++,就从后往前覆盖一遍f数组,看每个位置上的值是否更新

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1010;
 5 int n,m; //个数和背包容量
 6 int v[N],w[N]; //每个物品的体积和价值
 7 int f[N][N]; //表示状态
 8 
 9 int main()
10 {
11     cin >> n >> m;
12     
13     for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i]; //读入所有的物品
14     
15     // f[0][0 ~ m] = 0; //0个没有意义,且全局变量已经初始化
16     
17     //f[i][j] 表示从1~i的数,总价值不超过j的集合
18     for (int i = 1; i <= n; i ++ ) //枚举个数
19       for (int j = 0; j <= m; j ++ ) //枚举体积
20       {
21           f[i][j] = f[i - 1][j];
22           if(j >= v[i]) f[i][j] = max(f[i - 1][j],f[i - 1][j - v[i]] + w[i]); //判断是否为空集
23       }
24       cout << f[n][m];
25       
26       return 0;
27 }
View Code
复制代码

 

一维滚动数组表示

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1010;
 5 int n,m; //个数和背包容量
 6 int v[N],w[N]; //每个物品的体积和价值
 7 int f[N]; //表示状态
 8 
 9 int main()
10 {
11     cin >> n >> m;
12     
13     for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i]; //读入所有的物品
14     
15     // f[0][0 ~ m] = 0; //0个没有意义,且全局变量已经初始化
16     
17     //f[i][j] 表示从1~i的数,总价值不超过j的集合
18     for (int i = 1; i <= n; i ++ ) //枚举个数
19       for (int j = m; j >= v[i]; j -- ) //枚举体积
20       {
21           //f[j] = f[j],去掉了f[i]那一层
22           //要使得j >=v[i]才有结果,所以可以让循环从j = v[i]开始
23           //但由于j - v[i] < v[i],所以在这第i层循环中已经被遍历过了
24           //等价于   f[i][j] = max(f[i - 1][j],f[i][j - v[i]] + w[i]),但正确的是f[i - 1];
25           //所以将其反过来遍历一次,此时 j -v[i] > v[i],没有在之前被遍历过
26           //if(j >= v[i]) f[i][j] = max(f[i - 1][j],f[i - 1][j - v[i]] + w[i]); 
27           f[j] = max(f[j],f[j - v[i]] + w[i]);
28       }
29       cout << f[m] << endl;
30       
31       return 0;
32 }
View Code
复制代码

 

 

2.完全背包问题

详情请看代码

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 const int N = 1010;
 4 int f[N][N];
 5 int v[N],w[N];
 6 int main()
 7 {
 8     int n,m;
 9     cin>>n>>m;
10     for(int i = 1 ; i <= n ;i ++)
11     {
12         cin>>v[i]>>w[i];
13     }
14 
15     for(int i = 1 ; i <= n ;i ++)
16     for(int j = 0 ; j <= m ;j ++)
17     {
18         for(int k = 0 ; k*v[i] <= j ; k ++)
19             f[i][j] = max(f[i][j],f[i - 1][j - k * v[i]] + k * w[i]);
20     }
21 
22     cout<<f[n][m]<<endl;
23 }
24 
25 //优化思路(列举一下更新次序的内部关系)
26 /*  f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w ,  f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
27     f[i , j-v]= max(            f[i-1,j-v]   ,  f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
28 由上两式,可得出如下递推关系: 
29     f[i][j]=max(f[i,j-v]+w , f[i-1][j]) */
30     
31 //有上述关系,k循环则可以不要,核心代码简化
32 /*for(int i = 1 ; i <=n ;i++)
33 for(int j = 0 ; j <=m ;j++)
34 {
35     f[i][j] = f[i-1][j];
36     if(j-v[i]>=0)
37         f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
38 } */
39 
40 //对比01背包核心问题
41 /*for(int i = 1 ; i <= n ; i++)
42 for(int j = 0 ; j <= m ; j ++)
43 {
44     f[i][j] = f[i-1][j];
45     if(j-v[i]>=0)
46         f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);
47 } */
48 
49 //代码最终简化
50 /*for(int i = 1 ; i<=n ;i++)
51     for(int j = v[i] ; j<=m ;j++) //注意了,这里的j是从小到大枚举,和01背包不一样
52     {
53             f[j] = max(f[j],f[j-v[i]]+w[i]);
54     } */
55 
56 
57 #include<iostream>
58 using namespace std;
59 const int N = 1010;
60 int f[N];
61 int v[N],w[N];
62 int main()
63 {
64     int n,m;
65     cin>>n>>m;
66     for(int i = 1 ; i <= n ;i ++)
67     {
68         cin>>v[i]>>w[i];
69     }
70 
71     for(int i = 1 ; i<=n ;i++)
72     for(int j = v[i] ; j<=m ;j++)
73     {
74             f[j] = max(f[j],f[j-v[i]]+w[i]);
75     }
76     cout<<f[m]<<endl;
77 }
View Code
复制代码

 

3.多重背包问题

<1> 朴素版

复制代码
 1 #include <iostream>
 2 #include <algorithm>
 3 
 4 using namespace std;
 5 const int N = 110;
 6 
 7 int v[N], w[N], s[N];
 8 int f[N][N];
 9 int n, m;
10 
11 int main(){
12     cin >> n >> m;
13     for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i] >> s[i];
14 
15     for(int i = 1; i <= n; i ++){//枚举背包
16         for(int j = 1; j <= m; j ++){//枚举体积
17             for(int k = 0; k <= s[i] && j >=  k * v[i]; k ++){
18                     f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
19             }
20         }
21     }
22 
23     cout << f[n][m] << endl;
24 
25     return 0;
26 }
View Code
复制代码

 <2>二进制优化

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 
 4 const int N = 12010, M = 2010;
 5 
 6 int n, m;
 7 int v[N], w[N]; //逐一枚举最大是N*logS
 8 int f[M]; // 体积<M
 9 
10 int main()
11 {
12     cin >> n >> m;
13     int cnt = 0; //分组的组别
14     for(int i = 1;i <= n;i ++)
15     {
16         int a,b,s;
17         cin >> a >> b >> s;
18         int k = 1; // 组别里面的个数
19         while(k<=s)
20         {
21             cnt ++ ; //组别先增加
22             v[cnt] = a * k ; //整体体积
23             w[cnt] = b * k; // 整体价值
24             s -= k; // s要减小
25             k *= 2; // 组别里的个数增加
26         }
27         //剩余的一组
28         if(s>0)
29         {
30             cnt ++ ;
31             v[cnt] = a*s; 
32             w[cnt] = b*s;
33         }
34     }
35 
36     n = cnt ; //枚举次数正式由个数变成组别数
37 
38     //01背包一维优化
39     for(int i = 1;i <= n ;i ++)
40         for(int j = m ;j >= v[i];j --)
41             f[j] = max(f[j],f[j-v[i]] + w[i]);
42 
43     cout << f[m] << endl;
44     return 0;
45 }
View Code
复制代码

 

4.分组背包问题

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 110;
 5 
 6 int n,m;
 7 int v[N][N],w[N][N],s[N];
 8 int f[N];
 9 
10 
11 int main()
12 {
13     cin >> n >> m;
14     
15     for (int i = 1; i <= n; i ++ ) //枚举物品组
16     {
17         cin >> s[i];
18         for (int j = 0; j < s[i]; j ++ )
19          cin >> v[i][j] >> w[i][j];
20     }
21     
22     
23     for (int i = 1; i <= n; i ++ ) //枚举每一组物品
24        for (int j = m; j >= 0; j -- )
25           for (int k = 0; k < s[i]; k ++ ) //枚举每组物品中的每个选择
26           {
27               if(v[i][k] <= j)
28               f[j] = max(f[j],f[j - v[i][k]] + w[i][k]);
29           }
30     
31     cout << f[m] << endl;
32     return 0;
33 }
View Code
复制代码

 

posted @   rw156  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示