文章目录
- 线性Dp的定义
- AcWing 898. 数字三角形
- 思路
- CODE
- 正序
- 倒序
- AcWing 895. 最长上升子序列
- Dp 分析
- CODE
- AcWing 897. 最长公共子序列
- Dp 分析
- CODE
线性Dp的定义
处理起来是线性的(???),这部分交给ai老先生解释:
AcWing 898. 数字三角形
题目链接:https://www.acwing.com/activity/content/problem/content/1002/
思路
正序Dp:
- 状态表示
f[i][j]
:- 集合:表示的是到第 i i i行第 j j j列的数的路径和的集合。
- 属性:Max最大值
- 状态计算:
- 我们发现
f[i][j]
是由上一层的相邻两个数取最大值决定的,那么有方程: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j − 1 ] , f [ i − 1 ] [ j ] ) + a [ i ] [ j ] f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j] f[i][j]=max(f[i−1][j−1],f[i−1][j])+a[i][j]
- 我们发现
逆序Dp:
- 状态计算:
- 是由下面两个相邻的数的最大值来决定的,倒着算即可。
CODE
正序
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int n, a[N][N];
int main(){
cin >> n;
for(int i = 0; i <= n; ++i)
for(int j = 0; j <= i + 1; ++j)
a[i][j] = -INF;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
cin >> a[i][j];
for(int i = 2; i <= n; ++i)
for(int j = 1; j <= i; ++j){
a[i][j] = a[i][j] + max(a[i - 1][j], a[i - 1][j - 1]);
}
int res = -INF;
for(int j = 1; j <= n; ++j) res = max(res, a[n][j]);
cout << res << endl;
}
倒序
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 520, INF = 0x3f3f3f3f;
int n, a[N][N];
int main(){
cin >> n;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
cin >> a[i][j];
for(int i = n - 1; i >= 1; --i)
for(int j = 1; j <= i; ++j)
a[i][j] = max(a[i + 1][j], a[i + 1][j + 1]) + a[i][j];
cout << a[1][1] << endl;
}
AcWing 895. 最长上升子序列
题目链接:https://www.acwing.com/activity/content/problem/content/1003/
Dp 分析
- 状态表示
f[i]
:- 集合:在集合 [ 1 , i ] [1, i] [1,i] 之间的最长上升子序列的长度。
- 属性:Max
- 状态计算:
- 可以从哪些状态迁移过来:
- 在这个区间内的序列长度,即
f[k]
- 在这个区间内的序列长度,即
- 每个迁移状态怎么取值:
- 如果
a[i] > a[k]
:f[i] = f[k] + 1
- 如果
a[i] <= a[k]
:f[i] = f[k]
- 最后取最大值即可。
- 如果
- 可以从哪些状态迁移过来:
CODE
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1024, INF = 0x3f3f3f3f; // 定义常量N和INF
int s[N]; // 定义数组s,用于存储输入的序列
int f[N]; // 定义数组f,用于存储以每个元素结尾的最长递增子序列的长度
int main()
{
int n; // 定义变量n,表示序列的长度
scanf("%d", &n); // 输入序列的长度
for(int i = 1; i <= n; ++i) scanf("%d", &s[i]); // 输入序列的元素
for(int i = 1; i <= n; ++i) f[i] = 1; // 初始化数组f,每个元素的初始值为1
// 动态规划求解最长递增子序列
for(int i = 2; i <= n; ++i)
for(int j = 1; j <= i; ++j)
f[i] = max(f[i], (s[j] < s[i] ? (f[j] + 1) : 1));
int ans = -INF; // 定义变量ans,用于存储最长递增子序列的长度
for(int i = 1; i <= n; ++i) ans = max(ans, f[i]); // 找出最长递增子序列的长度
cout << ans << endl; // 输出最长递增子序列的长度
}
AcWing 897. 最长公共子序列
题目链接:https://www.acwing.com/activity/content/problem/content/1005/
Dp 分析
- 状态表示
f[i][j]
:- 集合:表示对 A A A 序列的前 i i i 个字母和 B B B 序列的前 j j j 个字母所包含的最大公共子序列长度。
- 属性:Max
- 状态计算:
- 集合划分:第
i
i
i 个和第
j
j
j 个字母是否相同。
- 相同:
f[i][j] = f[i - 1][j - 1] + 1
- 不同:
f[i][j] = max(f[i - 1][j], f[i][j - 1])
- 前 i − 1 i - 1 i−1 个字母与前 j j j 个字母的最大公共子序列。
- 前 i i i 个字母与前 j − 1 j - 1 j−1 个字母的最大公共子序列。
- 相同:
- 集合划分:第
i
i
i 个和第
j
j
j 个字母是否相同。
CODE
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1024; // 定义常量N
int f[N][N]; // 定义二维数组f,用于存储最长公共子序列的长度
char a[N], b[N]; // 定义字符数组a和b,用于存储输入的两个字符串
int main()
{
int n, m; // 定义变量n和m,表示两个字符串的长度
scanf("%d%d", &n, &m); // 输入两个字符串的长度
cin >> a + 1 >> b + 1; // 输入两个字符串
// 动态规划求解最长公共子序列
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j){
f[i][j] = max(f[i - 1][j], f[i][j - 1]); // 更新f[i][j]的值
// 如果a[i]等于b[j],则更新f[i][j]的值
if(a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;
}
cout << f[n][m] << endl; // 输出最长公共子序列的长度
}