请先阅读dp入门课一二,不然不好看懂。
上次的基础例题比较简单,就是已经讲过的背包。
直接给代码吧:
第一题:
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int f[N];
int main(){
int n,m;
cin >> n >> m;
for (int i = 0; i < m; i ++ ){
int v;
cin >> v;
for (int j = n; j >= v; j -- ){
f[j] = max(f[j],f[j - v] + v);
}
}
cout << n - f[n];
return 0;
}
第二题:
#include <bits/stdc++.h>
using namespace std;
const int N = 21000;
int n, m;
int f[N];
int main() {
cin >> m >> n;
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;
}
提高的那道例题,就是咱们几天要讲的内容。
大家先读一遍题:B3637 最长上升子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
大家先自己回忆一下上次讲的dp的4个步骤。
如果忘了,我就再列出来一次:
第一步,设置状态
第二步,推状态转移方程
第三步,确定边界
第四步,写代码
我们先思考几个问题:
问:该题如何设置状态?
答:f[i]表示以第i个数为结尾的答案的最大值。
问:状态如何转移?
答:首先枚举i,然后再枚举一个j,如果第j个数小于第i个数,我们就考虑第i个数接在第j个数后面,并且取个max。
问:这题边界如何处理?
答:这题无边界,但f[i]的初始值应该为1。
问:答案是什么?f[n]吗?
答:不是f[n],是在f数组当中取最大值。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N], f[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i ++ ) {
cin >> a[i];
}
for (int i = 1; i <= n; i ++ ) {
f[i] = 1;
for (int j = 1; j < i; j ++ ) {
if (a[j] < a[i]) {
f[i] = max(f[i], f[j] + 1);
}
}
}
int res = 0;
for (int i = 1; i <= n; i ++ ) {
res = max(res, f[i]);
}
cout << res;
return 0;
}
大家记住,该模型的名字就叫做最长上升子序列。
布置一道习题,也是下节课的内容: