线性dp例题

q1:数字三角形

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

        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5
输入格式

第一行包含整数 nn,表示数字三角形的层数。

接下来 nn 行,每行包含若干整数,其中第 ii 行表示数字三角形第 ii 层包含的整数。

输出格式

输出一个整数,表示最大的路径数字和。

数据范围

1≤n≤5001≤n≤500,
−10000≤三角形中的整数≤10000−10000≤三角形中的整数≤10000

输入样例:

5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5
输出样例:

30
线性dp

 1.初始化f[i,j]时,将该点的上左,上右都初始化为-1e9,避免处理边界

 ac代码

#include <iostream>
#include <cstring>
#include <algorithm>
 
using namespace std;
int n;
const int N = 510,M=1e9;
int a[N][N];
int dp[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=0;i<=n+1;i++)
    {
        for(int j=0;j<=i+1;j++)
        {
            dp[i][j]=-M;
        }
    }
    dp[1][1]=a[1][1];
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            dp[i][j]=max(dp[i-1][j-1]+a[i][j],dp[i-1][j]+a[i][j]);
        }
    }
    int m=-M;
    for(int i=1;i<=n;i++)
    {
        m=max(dp[n][i],m);
    }
    cout<<m;
    return 0;
}

q2:最长上升子序列​​​​​​

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤10001≤N≤1000,
−109≤数列中的数≤109−109≤数列中的数≤109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

 

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

using namespace std;
int n;
const int N=1111,M=1e9;
int a[N],f[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)   cin>>a[i];
    f[1]=1;
    for(int i=1;i<=n;i++)
    {
        f[i]=1;//f[i]最小为1,表示a[i]比前面的任何一个数都小
        for(int j=1;j<=i;j++)
        {
            if(a[i]>a[j])
            f[i]=max(f[i],f[j]+1);
        }
    }
    sort(f+1,f+1+n);
    cout<<f[n];
    return 0;
}

最长上升子序列二的数据范围为1e5,如果用上面的方法,时间复杂度为n^2,会超时 

q3: 最长上升子序列 II

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤1000001≤N≤100000,
−109≤数列中的数≤109−109≤数列中的数≤109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

观察得:

其存在一个性质,即,长度为i的最长上升子序列的结尾的数一定大于长度为i-1的最长上升子序列的结尾的值

q[1]=-2*M;
因为每个数最小为-1e9,保证最长上升子序列的最小长度为1

二分来寻找比第i个数小的最大的数 

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

using namespace std;
int n;
const int N=1e5+10,M=1e9;
int a[N];
int q[N];//q[i]表示长度为i的序列的结尾的最小的数
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)   cin>>a[i];
    int len=0;
    q[1]=-2*M;
    //二分找到小于a[i]的最大的数
    for(int i=1;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;
        }
        //r表示q[i]中的i,即小于a[i]的最大的数的序列的长度,+1表示序列加上a[i]
        len=max(len,r+1);//len表示最大的长度
        q[r+1]=a[i];
    }
    cout<<len;
    return 0;
}

q4:最长公共子序列

给定两个长度分别为 NN 和 MM 的字符串 AA 和 BB,求既是 AA 的子序列又是 BB 的子序列的字符串长度最长是多少。

输入格式

第一行包含两个整数 N 和 M。

第二行包含一个长度为 N 的字符串,表示字符串 A。

第三行包含一个长度为 M 的字符串,表示字符串 B。

字符串均由小写字母构成。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N,M≤10001≤N,M≤1000

输入样例:

4 5
acbd
abedc

输出样例:

3

 

 

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

using namespace std;
int n,m;
const int N=1111;
char a[N],b[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    scanf("%s%s",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];
}

q5:最小编辑距离 

给定两个字符串 AA 和 BB,现在要将 AA 经过若干操作变为 BB,可进行的操作有:

  1. 删除–将字符串 AA 中的某个字符删除。
  2. 插入–在字符串 AA 的某个位置插入某个字符。
  3. 替换–将字符串 AA 中的某个字符替换为另一个字符。

现在请你求出,将 AA 变为 BB 至少需要进行多少次操作。

输入格式

第一行包含整数 nn,表示字符串 AA 的长度。

第二行包含一个长度为 nn 的字符串 AA。

第三行包含整数 mm,表示字符串 BB 的长度。

第四行包含一个长度为 mm 的字符串 BB。

字符串中均只包含大写字母。

输出格式

输出一个整数,表示最少操作次数。

数据范围

1≤n,m≤10001≤n,m≤1000

输入样例:

10 
AGTCTGACGC
11 
AGTAAGTAGGC

输出样例:

4

 

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

using namespace std;
int n,m;
const int N=1111;
char a[N],b[N];
int f[N][N];
int ans=0x3f;
int main()
{
    scanf("%d%s%d%s",&n,a+1,&m,b+1);
    
    for (int i = 0; i <= m; i ++ ) f[0][i] = i;
    for (int i = 0; i <= n; i ++ ) f[i][0] = i;
    
    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);
            if(a[i]!=b[j])  f[i][j]=min(f[i][j],f[i-1][j-1]+1);
            else f[i][j]=min(f[i][j],f[i-1][j-1]);
        }
    }
    cout<<f[n][m];
}

 q6:编辑距离

给定 nn 个长度不超过 1010 的字符串以及 mm 次询问,每次询问给出一个字符串和一个操作次数上限。

对于每次询问,请你求出给定的 nn 个字符串中有多少个字符串可以在上限操作次数内经过操作变成询问给出的字符串。

每个对字符串进行的单个字符的插入、删除或替换算作一次操作。

输入格式

第一行包含两个整数 nn 和 mm。

接下来 nn 行,每行包含一个字符串,表示给定的字符串。

再接下来 mm 行,每行包含一个字符串和一个整数,表示一次询问。

字符串中只包含小写字母,且长度均不超过 1010。

输出格式

输出共 mm 行,每行输出一个整数作为结果,表示一次询问中满足条件的字符串个数。

数据范围

1≤n,m≤10001≤n,m≤1000,

输入样例:

3 2
abc
acd
bcd
ab 1
acbd 2

输出样例:

1
3

本题目是最小编辑距离的拓展,做法类似 

#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 15, M = 1010;

int n, m;
int f[N][N];
char str[M][N];

int edit_distance(char a[], char b[])
{
    int la = strlen(a + 1), lb = strlen(b + 1);

    for (int i = 0; i <= lb; i ++ ) f[0][i] = i;
    for (int i = 0; i <= la; i ++ ) f[i][0] = i;

    for (int i = 1; i <= la; i ++ )
        for (int j = 1; j <= lb; j ++ )
        {
            f[i][j] = min(f[i - 1][j] + 1, f[i][j - 1] + 1);
            f[i][j] = min(f[i][j], f[i - 1][j - 1] + (a[i] != b[j]));
        }

    return f[la][lb];
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%s", str[i] + 1);

    while (m -- )
    {
        char s[N];
        int x;
        scanf("%s%d", s + 1, &x);

        int res = 0;
        for (int i = 0; i < n; i ++ )
            if (edit_distance(str[i], s) <= x)
                res ++ ;

        printf("%d\n", res);
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑夜蔓蔓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值