动态规划背包详解——完全背包
作者:互联网
上一次我们讲了动态规划的定义以及01背包的算法和代码实现,没读过的请出门左转:https://www.cnblogs.com/YZYc/p/01Pack-Class-YPPAH.html
今天我们继续上一次的内容,讲一讲另外一种背包——完全背包。完全背包的定义其实和01背包十分相似,都是有n个物品,一个体积为m的背包,每个物品都有自己的体积和价值,但是它和01背包不同的是,完全背包每一个物品都可以放无限次,而01背包每个物品只能使用一次。完全背包的建表和查表也和01背包没有太大差异,不过填表的部分还是和01背包不太一样,让我们详细的来看一下完全背包填表的过程。
1.1:版本1 暴力解法O(n3) 注:这种方法在毒瘤数据下会TLE,主要理解思路即可
(1)状态f[i][j](同【01背包】):前i个物品,背包容量为j下的最优解(最大价值)。
(2)每一轮循环i都可以看作是对第i件物品的决策——选择多少个第i件物品。
(3)稍微不同的是完全背包允许多次选择一个物品,所以计算状态转移方程时需要枚举选择第i个物品
代码:
#include<bits/stdc++.h> using namespace std; const int MAXN = 10000; int v[MAXN]; // 体积 int w[MAXN]; // 价值 int f[MAXN][MAXN]; // f[i][j], j体积下前i个物品的最大价值 int main() { int n; int m; // 背包体积 cin >> n >> m; for(int i = 1; i <= n; i++) cin >> v[i] >> w[i]; for(int i = 1; i <= n; i++) for(int j = 0; j <= m; j++) { int amount = j / v[i]; // j体积时物品最多能选的次数 for(int k = 0; k <= amount; k++) // 枚举选择第i个物品的个数 f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); // 状态转移方程 } cout << f[n][m] << endl; return 0; }
1.2:版本2 优化时间O(n2)
实际上,我们在计算状态转移方程时不必多一个循环去单独枚举选择第i个物品个数。
状态转移方程如下:
推导过程如下:
我们计算的是前i个物品背包体积为j的最优解f[i][j],而前i-1个物品的最优解f[i-1][j]在上一轮循环中都已计算完毕,现在我们只需判断选择几个第i种物品得到的价值最大。我们改变一下变量,将j变成j-v,则有:
由以上两个式子就可以得到一个新的状态转移方程:
f[i][j]=max(f[i-1][j],f[i][j-v])
我们枚举体积j是从小到大的,那么我们在计算f[i][j]时,f[i][较大体积]总是由f[i][较小体积]更新而来。
最终代码如下:
#include<bits/stdc++.h> using namespace std; const int MAXN = 10000; int v[MAXN]; // 体积 int w[MAXN]; // 价值 int f[MAXN][MAXN]; // f[i][j], j体积下前i个物品的最大价值 int main() { int n; int m; // 背包体积 cin >> n >> m; for(int i = 1; i <= n; i++) cin >> v[i] >> w[i]; f[0][0] = 0; for(int i = 1; i <= n; i++) for(int j = 0; j <= m; j++) { f[i][j] = f[i - 1][j]; // 不选第i个物品 if(j >= v[i]) // 可以选择第i个物品,状态方程见上面推导 f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]); } cout << f[n][m] << endl; return 0; }
(代码来自:https://www.acwing.com/solution/content/1375/)
标签:背包,01,int,详解,MAXN,物品,体积,动态 来源: https://www.cnblogs.com/YZYc/p/FullPack-Class-YPPAH.html