OVSolitario-io

导航

DP衔接(拼串:进行长度构造)

状态之间视为点,状态的转移为点之间的有向边,则会构建一张DAG

无环:不会确定后面的值之后再去更新前面的值

根据上面思路:
无后效性: 当一个状态确定,则不会被后面的状态所影响

即算f[5]时,与f[5]前面的f[2],f[3]无关···即不关心前面(即使有多种算法)如何算出,只保证f[i]为最优

最优子结构: 子问题的最优解可以推导出整个问题的最优解

DP三要素:

  • 状态:描绘局面
  • 转移:状态间计算关系
  • 阶段:计算状态的顺序

对于DP思考路径,往往考虑状态的由来状态的去处

一维线性DP:状态定于与题设为线性相关DP

LIS为一条线,为一维线性DP,而如棋盘问题,为棋盘二维,为二维线性DP

再看LIS(n^2 => nlogn做法):维护LIS每个长度下最小结尾(单调) + 二分

对于朴素的LIS,会对其前面all元素都进行一次比较尝试更新,但其实对于长度为x + 1的值来说真正有用的只有前面长度为x的那几个值,发现对于相同长度的LIS,显然结尾的数越小越好(后面能接的数更多),那么在LIS长度相同时,我们保留最小的ai

动态的维护最小LIS的结尾

那么即得到下图:
截屏2025-08-31 18.01.42
此时当后面接7时,即将LIS5的9替换掉(更优),读2换3,读8则接到后面使LIS长度+1

LIS存在着单调性:当x<a'len(len为LIS长度)则替换,否则加入队尾
截屏2025-08-31 18.10.15
有着明显单调性,则这两个操作可以通过二分来实现

for(int i = 1; i <= n; ++ i) {
    //找第一个大于等于a[i]元素
    int pos = lower_bound(g + 1, g + len + 1, a[i]) - g;
    g[pos] = a[i];//将此为改为a[i]
    len = max(len, pos);//更新长度
}

二维DP

数字三角形,二维通过(i, j)两个数来确定位置

LCS:
对于LCS的两个序列,无法用一维来定位,二维:定义为f[i][j]表示为数列A的前i位与数列B的前j位组成的LCS
此时有情况:

  • 匹配:长度+1
  • 不匹配:从前面长的过渡过来
    截屏2025-08-31 19.15.17

再看LCS:重标号构造单调递增
发现若数列A,B为同列(1~n都各出现一次),则有对序列重标号,此时局面为求一序列与另一单调递增序列求LCS,那么就是求序列中的最长LIS即可
截屏2025-08-31 19.26.52

其中32145为abcde,12345为cbade
那么因为单调递增,求另一个最长ade,一定可以再单调序列取到

即当1~n都出现一次,则可转换为求LIS问题,在套用前面nlogn求LIS
但存在强限制:AB都为数列,但很多时AB都为字符串

编辑距离:P2758 编辑距离
两维,f[i][j]表示A的前i个字符变为B的前j个字符的最小操作次数,因此当为f[n][m]时则为A的前n个字符=B的前j个字符

对于三种操作:

  • 删除:f[i][j] = f[i - 1][j] + 1

即不要第i个字符

  • 插入:f[i][j] = f[i][j - 1] + 1

同理

  • 修改:f[i][j] = f[i - 1][j - 1] + [Ai != Bi]

将A的前i-1变为B的前j-1字符,再加上是否相等的修改(不想等则+1次操作)

#include <bits/stdc++.h>
using namespace std;
int min(int x, int y, int z) {
    return min(min(x, y), z);
}
int edit_dist_dp(string str1, string str2) {
    int m = str1.length();
    int n = str2.length();
    vector<vector<int>> dp(m + 1, vector<int> (n + 1));
    for(int i = 0; i <= m; ++ i) {
        for(int j = 0; j <= n; ++ j) {
            if(i == 0) {
                dp[i][j] = j;//当A中字符数为0,要插入j次来构造j
            }
            else if(j == 0) {//当B中字符为0,A要删除j次来构造j
                dp[i][j] = i;
            }
            else if(str1[i - 1] == str2[j - 1]) {//A=B直接转移,不操作
                dp[i][j] = dp[i - 1][j - 1];
            }
            else {//否则计算最小操作步数
                dp[i][j] = 1 + min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]);
            }
        }
    }
    return dp[m][n];
}

int main() {
    string str1, str2;
    cin >> str1 >> str2;
    cout << "Mininum number of operation:" << edit_dist_dp(str1, str2) << endl;
    return 0;
}

posted on 2025-08-31 17:45  TBeauty  阅读(1)  评论(0)    收藏  举报