动态规划专题

线性dp

2021-02-20  本文已影响0人  Tsukinousag

数字三角形

原题链接

一直走到底层,要求找出一条路径,使路径上的数字的和最大。

#include <iostream>
#include <algorithm>

using namespace std;

const int N=510;
const int INF=1e9;

int n;
int a[N][N],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;i++)//处理左边界溢出问题,左边界旁边初始化-∞
        for(int j=0;j<=i+1;j++)//处理在右边界溢出问题,右边界的旁边初始化为-∞
            dp[i][j]=-INF;
            

    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 res=-INF;
    for(int j=1;j<=n;j++)
        res=max(res,dp[n][j]);
    cout<<res<<endl;
    
    return 0;
}


最长上升子序列Ⅰ(模板)(O(n²))

原题链接

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

N=1000

#include<iostream>

using namespace std;

const int N=1010;
int a[N],dp[N];

int n;

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
        
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;//只有a[i]一个数
        for(int j=1;j<i;j++)
            if(a[j]<a[i])
            dp[i]=max(dp[i],dp[j]+1);
    }
    
    int res=0;
    for(int i=1;i<=n;i++)
        res=max(res,dp[i]);
        
    cout<<res;
        
    return 0;
}

最长上升子序列Ⅱ(单调性优化+二分查找)(O(nlogn))

原题链接

N=100000

优化算法Ⅰ:

1.利用DP求取针对最末位的元素的最长的子序列,如果子序列的长度相同,那么最末位的元素较小的在之后会更优(因为末位更小更加有机会接新的序列)

因此重新定义集合:

具体步骤:初始化dp[i],每个设置为INF,从头往后插入一个a[j],比较大小,dp[i]=min(d[i],a[j])进行更新得到单调递增序列,因为递增序列,所以可以二分查找更新位置

#include <iostream>
#include <algorithm>

using namespace std;

const int N=1e5+10;
const int INF=0x3f3f3f3f;

int n;
int a[N],dp[N];

int main()
{
    cin>>n;
    fill(dp,dp+n,INF);
    for(int i=0;i<n;i++)
        cin>>a[i];
    
    for(int i=0;i<n;i++)
        *lower_bound(dp,dp+n,a[i])=a[i];//注意lower_bound返回的是指针,在返回处直接赋值a[i]
    
    printf("%d\n",lower_bound(dp,dp+n,INF)-dp);//第一个无穷处-0处=长度
    
    return 0;
}

0 1 2 3 4 5
1 2 2 2 3 4
4-1=3

实操心得:长度为n的有序数组a中的k的个数

upper_bound(a,a+n,k)-lower_bound(a,a+n,k)=3,长度是3

*upper_bound(a,a+n,k)=3,返回了4处的值


最长公共子序列 (O(nm))

原题链接

#include <iostream>
#include <algorithm>

using namespace std;

const int N=1010;

int n,m;
char a[N],b[N];

int dp[N][N];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++) cin>>b[i];
    
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i]==b[j])
                dp[i][j]=dp[i-1][j-1]+1;
            else
                dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
        }
    }
    
    cout<<dp[n][m]<<endl;
    
    return 0;
}

最短编辑距离

原题链接

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

删除–将字符串A中的某个字符删除。
插入–在字符串A的某个位置插入某个字符。
替换–将字符串A中的某个字符替换为另一个字符。
现在请你求出,将A变为B至少需要进行多少次操作。

#include <iostream>
#include <algorithm>

using namespace std;
const int N=1010;

int n,m;
int dp[N][N];
char a[N],b[N];

int main()
{
    scanf("%d%s",&n,a+1);
    scanf("%d%s",&m,b+1);
    
    for(int i=0;i<=n;i++) dp[i][0]=i;//b无,删i次a
    for(int i=0;i<=m;i++) dp[0][i]=i;//a无,增i次a
    
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1);//1 2 操作
            //特判 第三种情况
            if(a[i]==b[j])
                dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
            else
                dp[i][j]=min(dp[i][j],dp[i-1][j-1]+1);
        }
    }
            
    cout<<dp[n][m]<<endl;
    return 0;
}

编辑距离

原题链接

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

using namespace std;

const int M=11,N=1010;

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

int solve(char a[],char b[])
{
    int lena=strlen(a+1);//此处是strlen(a+1) scanf("%s",a+1); printf("%d",strlen(a));输出是0
    int lenb=strlen(b+1);
    
    for(int i=0;i<=lena;i++) dp[i][0]=i;
    for(int j=0;j<=lenb;j++) dp[0][j]=j;
    
    for(int i=1;i<=lena;i++)
    {
        for(int j=1;j<=lenb;j++)
        {
            dp[i][j]=min(dp[i][j-1]+1,dp[i-1][j]+1);
            if(a[i]==b[j])
                dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
            else 
                dp[i][j]=min(dp[i][j],dp[i-1][j-1]+1);
        }
    }
    
    return dp[lena][lenb];
}

int main()
{
    cin>>n>>m;
    
    for(int i=0;i<n;i++)    scanf("%s",str[i]+1);
    
    while(m--)
    {
        char s[N];
        int t;
        scanf("%s%d",s+1,&t);
        
        int res=0;
        for(int i=0;i<n;i++)
            if(solve(str[i],s)<=t) res++;
            
        cout<<res<<endl;
    }
    return 0;
}

实操心得:
int lena=strlen(a+1)此处是strlen(a+1)
scanf("%s",a+1);
printf("%d",strlen(a));输出是0


上一篇下一篇

猜你喜欢

热点阅读