线性DP
线性DP:
状态维度:
- 答案一定能表示
- 答案可以被推出
以此为基础,维度越低越好(更少时复)
因此维度:从小->大考虑
DP存方案:记录转移即可,例子在LIS后面
LIS
考虑LIS问题,我的上一步一定是我前面的某一个值,将我接在它后面,LIS长度+1
定义:dp[i]是以i结尾的LIS长度
考虑:
- 若j < i,aj < ai,则进行拼接
状态转移方程: 我 = 比我小的序列 + 1
从前往后顺序求解,保证了计算a[i]时,i前面allj的LISa[j]已算出,此时a[i] = a[j] + 1,考虑all情况a[i] = max(a[j] + 1);
点击查看代码
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);
}
}
res = max(res, f[i]);
}
存方案:
点击查看代码
for(int i = 1;i <= n; ++ i)
{
f[i] = 1;
g[i] = 0;
for(int j = 1; j < i; ++ j) {
if(a[j] < a[i]) {
if(f[i] < f[j] + 1) {//只有更优才会存储
f[i] = f[j] + 1;
g[i] = j;//记录从哪个状态转移而来
}
}
}
}
int k = 1;//求最优解下标,以k结尾
for(int i = 1; i <= n; ++ i) {
if(f[k] < f[i]) {
k = i;
}
}
printf("%d\n", f[k]);
//最优解以k结尾,往回推(a[]为值,g[]为下标)
for(int i = 0, len = f[k]; i < len; ++ i) {//输出的是方案逆序
printf("%d ", a[k]);
k = g[k];
}
LIS()
LCS
两序列A,B两维表示,对于a和b序列LCS以AB结尾的LCS上一个值一定是A序列中的i,且存在B序列中的j
定义:dp[i,j]为a序列前i个字母,b序列前j个字母构成的子序列
发现对于Ai,Bj有三种可能:
- 用Ai和Bj,if Ai = Bj
- 不用Ai
- 不用Bj
从前往后去看即可
点击查看代码
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]);
if(a[i] == b[j]) {
f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
}
}
}
特殊的(1 ~ n)LCS:LCS重标号转LIS问题
p2758编辑距离
2串=>2维,为将a前i个字符,变为b的前j个字符的最小操作次数
浙公网安备 33010602011771号