今天,我们要讲一个算法,他的名字叫做动态规划,也叫dp。
不知道大家学过贪心算法没有,没学过可以自己查一下。
先看一道例题,了解一下dp:
大家先尝试一下贪心算法。
如果你按着 v[i] 的大小来选,我可以举出反例:
3 5
1 2
1 3
5 100000
答案是100000,你的程序对了吗?
如果你按着 w[i] 来选,我也可以举出反例:
3 5
1 10
5 12
3 1
答案是50,你的程序对了吗?
这道题,如果你dfs或bfs,直接超时。
而用贪心,又会错。
于是我们引入新概念:
DP!
我们可以开一个二维数组,就叫做f吧。
f[i][j]代表在前i个中用j元钱能获得的最大价值。
假设我们知道f[i][j]之前的所有数,如何求出f[i][[j]?
我们来看一看。
首先,f[i][j]=f[i-1][j].
有人会问:为什么呢?
大家想想,我这步假设啥也不去,那答案就是f[i-1][j],所以我们可以让他先等于这个值,再枚举每件物品,看选择它或不选择他的答案那个大,就选择那个。
所以代码就是这样:
#include <bits/stdc++.h>
using namespace std;
const int N = 5010;
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 = 1; j <= m; j ++ ){
f[i][j] = f[i - 1][j];
if (j - v[i] >= 0){
f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
}
}
cout << f[n][m] << endl;
return 0;
}
我们在思考一下,是不是i的那一维度完全用不到,我们就可以把i的那一维减下来。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int f[N];
int main(){
int n,m;
cin >> n >> m;
for (int i = 1; i <= n; i ++ ){
int v,w;
cin >> v >> w;
for (int j = m; j >= v; j -- ){
f[j] = max(f[j],f[j - v] + w);
}
}
cout << f[m];
return 0;
}
好,今天就讲到这里,给大家布置一个习题:3. 完全背包问题 - AcWing题库