今天,我们要讲一个算法,他的名字叫做动态规划,也叫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
- 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
- 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题库