01背包
初学者学DP的时候都会接触到背包问题吧哈哈,我理解起来是真的费劲不过坚持就是胜利,让我们一起来学习吧!
首先我看过y总的课,Acwing.com y总背包讲的很好,其中他提供给我了一种去分析动态规划的一种思考方式,我觉得很好。
我们要考虑一道题中,他的状态表示,和状态表达。
而状态表示中,又分为他的集合,和属性。
在01背包中状态表示就会用一个f[i][j]来表示,那在这个集合中,表示我能用到的所有选法,然后条件就是我只能在前i个物品中选,总体积小于等于j的情况下我们所有选法的最大价值,因为01背包中要求最大价值。
然后属性,就是最大值Max
再看如何计算状态,就要看看集合的划分,
当我们的背包容量够的时候,也就是j>=v[i]的时候我们就可以选上第i件物品。那我们就可以这么表示f[i-1][j-v[i]]+w[i],这个式子的意思就是本来可以拿上i物品是吧,应该是f[i][j],但是在大佬们的努力下发现这样算就会很麻烦,所以我们可以这样,i-1之后,就说明我们把第i件物品拿出来了,那么j也要减去i物品的体积对吧,因为我们选不上i了,说以j-v[i],也就是减去第i件物品的体积,那我们减去了i,是不是还要给他加回来,所以后面再加上他的价值w[i],所以综上所述,f[i-1][j-v[i]]+w[i];
那么如果第i件物品大于了j的体积,就选不上i了,所以f[i][j]就等于f[i-1][j];
那么我们的状态计算就表示清楚了,接下来就该看代码了。
那为什么我们要讲二维的方法呢,有些人说不是有一维的办法吗,因为一维的办法是从二维的办法中优化得到的,所以我们一定要了解到他的朴素算法,再去进行优化,我们小白要从他的思维方式一步步的学习他。
#include<iostream> using namespace std; const int N = 1011; int n,m; int v[N],w[N]; int f[N][N]; int main() { 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++) { f[i][j]=f[i-1][j]; if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]); } cout << f[n][m] << endl; return 0; }
这里也给出一维数组的解决方法。
#include <iostream> #include <algorithm> using namespace std; const int N = 1010; int n, m; int v[N], w[N]; int f[N]; int main() { 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 = m; j >= v[i]; j -- ) f[j] = max(f[j], f[j - v[i]] + w[i]); cout << f[m] << endl; return 0; }