01背包问题/动态规划(快速入门)
前言:背包问题是dp(动态规划)经典入门题目集合,里面的01背包问题也是一切的起点,作者当初学习此算法的时候,把各种教学视频看了十几遍,加上老师的讲解,还有很多博客,最后还是一知半解,但是随着时间积累,慢慢的在许久之后明白了这个算法,这也是我的一个学习算法的建议,在入门一个算法的时候,不必短时间内理解它的含义,记住模板,然后在未来的某个时刻,会理解它的。
以题目入门
01背包问题
有 N 件物品和一个容量是 V的背包。每件物品只能使用一次。
第 i件物品的体积是 vi,价值是wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5 1 2 2 4 3 4 4 5
输出样例
8
提交代码:
#include<bits/stdc++.h> using namespace std; const int N = 1010; int n, m; // 物品的数量,背包的容积 int v[N], w[N]; // v是每个物品体积的数组,w是每个物品价值的数组 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; }
解析
我给的代码是一个01背包问题的标准模板,下面的便是我对该模板的解释。
首先对题目分析,有N件物品,和一个容器为V的背包,,然后每件物品只能拿一次,然后每件物品的体积和价值不一样,求怎么拿才是价值最大的,这也是一个很现实的问题。
f[n][m]是什么意思了,拿f[i][j]的意思为,意思很简单,就是对于容量为i,背包容量为j的时候,最大的价值是多少。
关键代码:
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;
最后输出f[n][m]的含义是,当物品数量为n和背包容积为m的时候,最大的价值。
然后外面的两层for循环是枚举,n,m的情况,其实dp问题可以很简单,主要是需要找到对应的数学表达式,这个肯定比高中数学题目简单。
这个题目的数学表达式是什么了。
f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i])
f[i - 1][j]的含义为:当物品数量为i-1,然后背包容量为j(这里代表的是第i件物品不选)的时候的最大价值,如果第i件物品不选的话,
那么f[i][j]就直接等于f[i - 1][j]。
f[i - 1][j - v[i]] + w[i]的含义为:相比于f[i - 1][j]区别在于,他选了第i件,第i件的体积大小为v[i],然后价值为w[i],因为选了
第i件所以j-v[i],体积变小了,然后对于他的价值多了w[i]。
所以每次最后的表达式就是f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]),只是需要注意的是,j不一定每次大于v[i],
为了防止数组下标小于0,所以需要一个if判断一下.