注:请先阅读dp入门课(一),不然不好看懂。
dp入门课1:dp入门课(一)-CSDN博客
简单回顾:
上次我们讲解了dp的入门,01背包问题。
这次,我们要深入dp。
我们先讲一下上次的习题。
大家先看一遍题目:3. 完全背包问题 - AcWing题库
这道题和01背包差不多,只不过是每件物品有无限个。
首先,我们先尝试一下,写出这样的代码:
#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 -- ){
for (int k = 0; k * v <= j; k ++ ){
f[j] = max(f[j],f[j - k * v] + k * w);
}
}
}
cout << f[m];
return 0;
}
不过,这份代码超时了,我们得优化一下。
其实,我们会发现,可以不用枚举k,直接把j的枚举顺序改一下,就可以AC了.
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int f[N];
int main(){
int n,m;
cin >> n >> m;
while (n -- ){
int v,w;
cin >> v >> w;
for (int j = v; j <= m; j ++){
f[j] = max(f[j],f[j - v] + w);
}
}
cout << f[m];
return 0;
}
好,我们进入正题。
我们先定义一个概念:这些数组里面的值叫做状态,通过一个状态得出了另一个状态的公式叫做状态转移方程。
我现在给大家讲一下写dp的步骤。
第一步,设置状态,例如说f[i]代表选i个能得到的最大值。
第二步,推出状态转移方程。
第三步,确定边界,避免边界出错
第四步,写代码。
好,dp的步骤已经给大家讲了,不过要做到这几点还是得多做题。
今天讲到这里,布置几道习题。
基础:P1049 [NOIP2001 普及组] 装箱问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
提高: