ch4(DP)——线性DP(最长上升子序列LIS、最长公共子序列LCS、最短编辑距离)

1.自然语言描述
线性DP的特点是状态在线性结构上转移;其问题形式多种多样,只有几个典型的问题有固定的模板,更一般的问题是具体问题具体分析。

最长上升子序列LIS:用f[i]表示以i结尾的元素最长上升子序列的长度;状态集合划分,是否能成为原先以s[1],s[2],…,s[i-1]结尾的最长上升子序列的新末尾元素,从而有状态转移方程f[i]=min(f[i],f[j]+1)(1<=j<i,s[j]<s[i])注意根据定义,f[i]初始化为1。

最长公共子序列LCS:用f(i,j)表示序列A以i结尾的前缀和序列B以j结尾的前缀的最长公共子序列的长度;状态集合划分,看A[i]和B[j]是否相等,如果A[i]=B[j],则说明公共子序列长度加1,在f(i-1,j),f(i,j-1),f(i-1,j-1)+1中选最大值;若不相等,则在f(i-1,j),f(i,j-1)中选最大值。

最短编辑距离(K近似匹配):字符串A通过删除、插入和替换三种操作变换到字符串B。用f(i,j)表示字符串A以i结尾的前缀和字符串B以j为结尾的前缀的最短编辑距离(最小改动数)状态集合划分,A(1,i)通过删除A[i]才能做到和B(1,j)匹配所以在这之前A(1,i-1)与B(1,j)已经匹配,则编辑距离为f(i-1,j)+1;A(1,i)通过插入A[i](其实就是B[j])才能做到和B(1,j)匹配所以在这之前A(1,i)与B(1,j-1)已经匹配,则编辑距离为f(i,j-1)+1;类似的,若A[i]!=B[j],A(1,i)要通过替换A[i]进行匹配,那么编辑距离为f(i-1,j-1)+1,若A[i]=B[j],则编辑距离仍为f(i-1,j-1)。

2.代码描述
题目:Acwing.898 数字三角形题目链接

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int MAXN=510;

int s[MAXN][MAXN],f[MAXN][MAXN];

int main(void)
{
    int n;
    scanf("%d",&n);
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&s[i][j]);
            
    for(int i=0;i<=n;i++)//初始化f,因为s有负数,所以将f初始化为一个很小的数
        for(int j=0;j<=n+1;j++)//注意每行要延伸初始化1个单位,因为三角形右侧边上的元素进行最小值运算时要用,不能初始化为0
            f[i][j]=-1e9;
            
    f[1][1]=s[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]+s[i][j],f[i-1][j]+s[i][j]);//从左上方走过来的路径和右上方走过来的路径中取最大值
            
    int ans=0;
    for(int i=1;i<=n;i++)//在所有路径中的最大路径值
        ans=max(ans,f[n][i]);
        
    cout<<ans<<endl;
    
    return 0;
}

题目:Acwing.895 最长上升子序列题目链接

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN=1010;

int n,s[MAXN],f[MAXN];//f[i]记录以i元素结尾的最长上升子序列的长度

int main(void)
{
    cin>>n;
    
    for(int i=1;i<=n;i++)
        cin>>s[i];
       
    for(int i=1;i<=n;i++){
        f[i]=1;
        for(int j=1;j<i;j++)
            if(s[j]<s[i])
                f[i]=max(f[i],f[j]+1);
    }
    
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,f[i]);
    cout<<ans<<endl;
    
    return 0;
}

题目:Acwing.896 最长上升子序列II题目链接

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN=1e5+10;

int n,a[MAXN],q[MAXN];//q存储序列中所有上升子序列的最大元素的最小值

//将所有上升子序列的最后一个元素保存下来,q一定是单调递增的
//证明:若q[i]<=q[i-1],即不是单调递增的,则说明长度为i的上升子序列的第i-1项同样也<=q[i-1];
//q[i-1]并不是最小值,形成矛盾。
//所以q一定单调递增。
//这样,序列的所有元素都寻找当前q中小于自己的最大值的位置,q就确定了。

int main(void)
{
    cin>>n;
    
    for(int i=0;i<n;i++)
        cin>>a[i];
        
    q[0]=-2e9;//长度为0的最后一个元素初始化为一个很小的数
    int len=0;
    for(int i=0;i<n;i++){
        int l=0,r=len;
        while(l<r){
            int mid=l+r+1>>1;
            if(q[mid]<a[i])
                l=mid;
            else
                r=mid-1;
        }
        len=max(len,r+1);//长度r变为长度r+1
        q[r+1]=a[i];
    }
    
    cout<<len<<endl;
    
    // for(int i=1;i<=len;i++)
    //     cout<<q[i]<<' ';
    
    return 0;
}

题目:Acwing.902 最短编辑距离题目链接

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN=1010;

int n,m,f[MAXN][MAXN];//f[i][j]:将a(1~i)串变成b(1~j)串所需要的最少编辑数
char a[MAXN],b[MAXN];

int main(void)
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
        
    cin>>m;
    for(int j=1;j<=m;j++)
        cin>>b[j];
        
    for(int i=0;i<=n;i++)//按照定义初始化f
        f[i][0]=i;
    for(int j=0;j<=m;j++)
        f[0][j]=j;
        
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);//删除后匹配:f[i-1][j]+1   插入后匹配:f[i][j-1]+1
            if(a[i]==b[j])//本来就匹配
                f[i][j]=min(f[i][j],f[i-1][j-1]);
            else//替换后匹配:f[i-1][j-1]+1
                f[i][j]=min(f[i][j],f[i-1][j-1]+1);
        }
        
    cout<<f[n][m]<<endl;
    
    return 0;
}

题目:Acwing.899 编辑距离题目链接

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN=15,SIZE=1010;

int n,q,f[MAXN][MAXN];
char s[SIZE][MAXN];
//最短编辑距离的应用,每次询问看所有字符串与询问字符串的最短编辑距离,若能够<=限制,则说明可以在限制次数内进行变换
int main(void)
{
    cin>>n>>q;
    
    for(int i=1;i<=n;i++)
        cin>> s[i]+1;
        
    while(q--){
        char str[MAXN];
        int x;
        cin>> str+1>>x;
        
        int ans=0;
        for(int i=1;i<=n;i++){
            
            memset(f,0,sizeof(f));//每次要初始化
            int len_s=strlen(s[i]+1),len_str=strlen(str+1);
            
            for(int j=0;j<=len_s;j++)
                f[j][0]=j;
            for(int j=0;j<=len_str;j++)
                f[0][j]=j;
                
            for(int k=1;k<=len_s;k++)
                for(int l=1;l<=len_str;l++){
                    f[k][l]=min(f[k-1][l]+1,f[k][l-1]+1);
                    if(s[i][k]!=str[l])
                        f[k][l]=min(f[k][l],f[k-1][l-1]+1);
                    else
                        f[k][l]=min(f[k][l],f[k-1][l-1]);
                }
                
            if(f[len_s][len_str]<=x)
                ans++;
        }
        cout<<ans<<endl;
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值