线性DP

1. 数字三角形

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5

从上到下:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=510,INF = 1e9;
/**
    DP
*/
int w[N][N],f[N][N];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++) cin>>w[i][j];
    }
    for(int i = 0;i<=n;i++){
        for(int j=0;j<=i+1;j++) f[i][j] = -INF;
    }
    f[1][1] = w[1][1];
    for(int i=2;i<=n;i++){//层数
        for(int j=1;j<=i;j++){
            f[i][j]=max(f[i-1][j-1]+w[i][j],f[i-1][j]+w[i][j]); //左上方 +w[i][j]  ,   右上方 + w[i][j]
        }
    }
    int res=-INF;
    for(int i=1;i<=n;i++) res=max(res,f[n][i]);
    cout<<res;
    return 0;
}

从下到上:

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=510;
/**
    DP
*/
int w[N][N],f[N][N];//f[i][j]表示所有路线的最大值,  从底向上
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<=i;j++) cin>>w[i][j];
    }
    for(int i=0;i<n;i++)f[n][i] = w[n][i];//初始化 最下一层f[n][i]最大值即为w[n][i]
    for(int i=n-1;i>=0;i--){
        for(int j=0;j<=i;j++){
            f[i][j]=max(f[i+1][j]+w[i][j],f[i+1][j+1]+w[i][j]); //左下方 +w[i][j]  ,   右下方 + w[i][j]
        }
    }
    cout<<f[0][0];
    return 0;
}

2.最长上升子序列 O( n 2 n^2 n2

给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少
在这里插入图片描述

/*
    状态表示 f[i] : 集合:所有以第i个数结尾的上升子序列
                    属性:Max
    状态计算        以倒数第二个数作为划分依据 0、1、2、···、i-1
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,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=0;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;
}

扩展:求最长上升子序列是哪一个

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,a[N],f[N],g[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=0;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;
    for(int i=1;i<=n;i++){
        if(f[k]<f[i]) {
            k = i;
        }
    }
    cout<<f[k];//输出最长上升子序列长度
    //输出最长上升子序列
    for(int i =1 , len = f[k] ;i<=len;i++){
        cout<<a[k]<<" ";
        k = g[k];
    }
}

3.最长上升子序列 II

4.最长公共子序列

给定两个长度分别为N和M的字符串A和B,求既是A的子序列又是B的子序列的字符串长度最长是多少
在这里插入图片描述
按照a[i]b[j]是否包含在该子序列中进行划分,如上图,四种情况,第一种不选a[i]不选b[j]的情况为f[i-1][j-1],最后一种选a[i]也选b[j]这种情况为f[i-1][j-1]+1(此时a[i]=b[j]) ;对于第二种情况和第三种情况,f[i-1][j]表示在第一个序列前i-1个字母中出现,第二个序列前j个字母中出现的子序列,这种情况一定包含不选a[i]b[j]的情况,且f[i-1][j]一定属于f[i][j],最终求的是f[i][j]的最大值,那么就可以用f[i-1][j]代替不选a[i]选择b[j]这种情况,同理也可以用f[i][j-1]代替选择a[i]不选b[j]这种情况。同时对于f[i-1][j-1]这种情况根据上述分析也一定被 f[i-1][j]f[i][j-1]包括
综上:f[i][j] = Max(f[i-1][j], f[i][j-1]) 如果a[i] =b[j]那么再取 f[i][j] = Max(f[i-1][j], f[i][j-1] ,f[i-1][j-1] + 1)

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n , m;
char a[N],b[N];
int f[N][N];
int main(){
    cin >> 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]);+
            if(a[i] == b[j]) f[i][j] = max(f[i][j] , f[i-1][j-1] + 1);
        }
    }
    cout << f[n][m];
    return 0;
}

5.编辑距离

6.最短编辑距离

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值