DP专题

树形dp

P2014 选课

题目链接:https://www.luogu.org/problem/P2014

 树形dp一般分为两种 一种是选节点类 一种是树形背包类 这一题是典型的树形背包类

我们可以发现这个是森林而不是树状结构 所以我们可以虚拟一个节点0 这个节点成为新的根节点

树形dp就是从底部到顶部 每棵子树选最优解 然后向根节点转移 这一过程可以用dfs实现

接下来就是推转移方程

dp[i][j]定义以i为子树选j个节点的方案数 dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[v][k]) v为i的子树中的节点 但是这里要注意一点 这里有一个依赖关系 如果我们要选择i的子树 那么必定要选择i 所以转移方程实际上 dp[i][j]=max(dp[i][j],dp[i][j-k-1]+dp[v][k]) 所以我们只要控制j-k>=1即可 然后对于当前以u为根节点的子树来说 对于方案数 相当于一维背包 所以要思考是顺序还是倒序 我们可以发现 如果是j=1~m 那么dp[u][j]会被前面已经算出的dp[u][j-k]影响(相当于选了多次)所以要倒序枚举

而且对于建图的过程 要考虑清楚是单向边还是双向边 如果指定了根节点就是单向边 如果未指定根节点(只说明了两个节点连接)那么就是双向边

且对于双向边 必须用一个变量father控制 因为双向边的dfs遍历会导致一直在一条边的两个点遍历 比如这一题

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 300+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int tot,head[maxn];
int w[maxn],dp[maxn][maxn];
int n,m;
struct Edge
{
    int dest,next;
} edge[maxn*2];

void add(int u,int v)
{
    edge[++tot].next=head[u];
    edge[tot].dest=v;
    head[u]=tot;
}

void dfs(int u)
{
    dp[u][1]=w[u];
    for(int i=head[u]; i; i=edge[i].next)
    {
        int v=edge[i].dest;
        dfs(v);
        for(int j=m; j>=1; j--) //根节点选了j个
        {
            for(int k=j; k>=0; k--) //子树选了k个
                    dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]);
        }
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    memset(head,0,sizeof(head));
    for(int i=1; i<=n; i++)
    {
        int a,b;
        scanf("%d %d",&a,&w[i]);
        add(a,i);
    }
    m++;
    dfs(0);
    printf("%d\n",dp[0][m]);
}

P2015 二叉苹果树

题目链接:https://www.luogu.org/problem/P2015

对比上一题 这题不一样的就是双向边 所以需要一个fa变量使dfs除了回溯不会向上搜索 然后还需要注意根节点是一定需要选择的 所以选择的节点数要+1

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 300+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int tot,head[maxn];
int w[maxn],dp[maxn][maxn];
int n,m;
struct Edge
{
    int dest,next,weight;
} edge[maxn*2];

void add(int u,int v,int w)
{
    edge[++tot].next=head[u];
    edge[tot].dest=v;
    edge[tot].weight=w;
    head[u]=tot;
}

void dfs(int u,int fa)
{
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].dest;
        if(v==fa) continue;
        int d=edge[i].weight;
        dp[v][1]=d;
        dfs(v,u);
        for(int j=m;j>=1;j--)
        {
            for(int k=j-1;k>=0;k--)
            {
                dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]);
            }
        }
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    m++;
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    dfs(1,1);
    printf("%d\n",dp[1][m]);
}

 

状压dp

P1879 [USACO06NOV]玉米田Corn Fields

题目链接:https://www.luogu.org/problem/P1879

一道最基础的状压dp 但是我是最近才搞懂的

通过十进制转二进制将每一行的地图压缩成一个十进制位数 其中1代表土地肥沃 0代表土地贫瘠

枚举状态 其中状态中的1代表种草 0代表不种草

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
 
#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 15;
const int mod = 1e8;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int mp[maxn][maxn],dp[maxn][1<<maxn];
int f[maxn];
int cnt[1<<maxn];
int tot;
bool check(int x)
{
    if(x&(x<<1))
        return 0;
    if(x&(x>>1))
        return 0;
    return 1;
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
            scanf("%d",&mp[i][j]);
    }
    for(int i=0; i<(1<<m); i++)
    {
        if(check(i))
            cnt[++tot]=i;
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
            f[i]=(f[i]<<1)+mp[i][j];
    }
    for(int i=1; i<=tot; i++)
    {
        if((f[1]&cnt[i])==cnt[i])
            dp[1][i]=1;
    }
    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<=tot; j++) //枚举这一行的状态
        {
            if((cnt[j]&f[i])==cnt[j])
            {
                for(int k=1; k<=tot; k++) //枚举上一行的状态
                {
                    if(!(cnt[j]&cnt[k]))
                    {
                        dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=tot;i++)
        ans+=dp[n][i],ans%=mod;
    printf("%d\n",ans);
}

  

P1896 [SCOI2005]互不侵犯

题目链接:https://www.luogu.org/problem/P1896

这个比上一题只是多枚举了一个需要放的国王数 要多开一组记录放的国王数 

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
 
#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 10;
const int mod = 1e8;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
ll dp[maxn][1<<maxn][81];
int num[1<<maxn];
int cnt[1<<maxn];
bool check(int x)
{
    if(x&(x<<1))
        return 0;
    if(x&(x>>1))
        return 0;
    return 1;
}
bool check(int x,int y)
{
    if(x&y)
        return 0;
    if(x&(y>>1))
        return 0;
    if(x&(y<<1))
        return 0;
    return 1;
}
int main()
{
    int n,k,tot=0;
    scanf("%d %d",&n,&k);
    for(int i=0; i<(1<<n); i++)
    {
        if(check(i))
            cnt[++tot]=i;
    }
    //处理每一个状态有多少个国王
    for(int i=1; i<=tot; i++)
    {
        for(int j=0; j<n; j++)
        {
            if((cnt[i]>>j)&1)
                num[i]++;
        }
    }
    //对于第一行
    for(int i=1; i<=tot; i++)
    {
        if(num[i]<=k)
            dp[1][i][num[i]]=1;
    }
    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<=tot; j++) //枚举当前行状态
        {
            for(int z=1; z<=tot; z++) //枚举上一行状态
            {
                if(check(cnt[j],cnt[z]))
                {
                    for(int nn=num[j]; nn<=k; nn++) //枚举king的个数
                    {
                        dp[i][j][nn]+=dp[i-1][z][nn-num[j]];
                    }
                }
            }
        }
    }
    ll sum=0;
    for(int i=1; i<=tot; i++)
    {
        sum+=dp[n][i][k];
        //printf("%d\n",sum);
    }
    printf("%lld\n",sum);
}  

Maximum Sum

题目链接:https://codeforces.com/gym/101853/problem/E

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 18;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int mp[maxn][maxn],dp[maxn][1<<maxn],cnt[1<<maxn];
bool check(int x)
{
    if(x&(x<<1)) return 0;
    if(x&(x>>1)) return 0;
    return 1;
}
bool check(int x,int y)
{
    if(x&y) return 0;
    if(x&(y<<1)) return 0;
    if(x&(y>>1)) return 0;
    return 1;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        memset(mp,0,sizeof(mp));
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&mp[i][j]);
            }
        }
        int top=0;
        for(int i=0;i<(1<<n);i++)
        {
            if(!check(i)) continue;
            cnt[++top]=i;
        }
        //第一行的处理
        for(int i=1;i<=top;i++)
        {
            for(int j=0;j<n;j++)
            {
                if((cnt[i]>>j)&1)
                {
                    dp[1][i]+=mp[1][n-j];
                    //printf("wa=%d\n",dp[1][i]);
                }
            }
        }
        for(int i=2;i<=n;i++)
        {
            for(int j=1;j<=top;j++) //枚举这一行的状态
            {
                for(int k=1;k<=top;k++) //枚举上一行的状态
                {
                    if(!check(cnt[j],cnt[k])) continue;
                    int sum=0;
                    for(int p=0;p<n;p++)
                    {
                        if((cnt[j]>>p)&1)
                            sum+=mp[i][n-p];
                    }
                    dp[i][j]=max(dp[i][j],dp[i-1][k]+sum);
                    //printf("wa=%d\n",dp[i][j]);
                }
            }
        }
        int ans=0;
        for(int i=1;i<=top;i++)
            ans=max(ans,dp[n][i]);
        printf("%d\n",ans);
    }
}

  

数位dp

P2657 [SCOI2009]windy数

题目链接:https://www.luogu.org/problem/P2657

 首先看a,b的数据范围是2e9 所以肯定不能从l-r遍历 考虑数位拆分

我们可以先把范围内所有可能的windy数预处理 然后后续直接加上预处理的答案即可

然后就是处理A,B数

定义dp[i][j]为第i位 最高位为j的方案数

a.A,B的长度等于1 ans=i+1 直接返回

b.A,B的长度大于1 ans=10 ( 0~9 )

然后分三种情况处理

1.从2~len-1位 此时所有的 j 从1~9都满足 这里一开始我没明白为什么不是从0开始 因为这里计算的就是满足的个数 既然满足 首先就必须是个数 而超过两位数的不会以0为最高位

2.len位 且j<a[len] 此时从1~a[len]-1累加方案数即可

3.len位 且j=a[len] 此时我们就从len-1位往后递推 每一次固定一位

举个例子 1569 我们先固定1 然后len-1位从0枚举到4 即10xx 11xx 12xx 13xx 14xx 然后固定5 len-2位从0枚举到5 即 150xx 151xx 152xx 153xx 154xx 155xx

但是这里有个坑点 即假如a[i+1]-a[i]小于2 就可以直接返回了 拿1569来说 枚举到len-2的时候 发现6-5<2 所以156x 后面不可能有满足的了

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 15;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int dp[maxn][maxn];
int a[maxn];

void init()
{
    for(int i=0;i<=9;i++) dp[1][i]=1;
    for(int i=2;i<=10;i++) //枚举有多少位
    {
        for(int j=0;j<=9;j++) //枚举当前位
        {
            for(int k=0;k<=9;k++) //枚举上一位
            {
                if(abs(j-k)>=2) dp[i][j]+=dp[i-1][k];
            }
        }
    }
}

int work(int x)
{
    int len=0;
    int ans=0;
    memset(a,0,sizeof(a));
    while(x)
    {
        a[++len]=x%10;
        x/=10;
    }
    //printf("len=%d\n",len);
    if(len<=1) return x+1;
    for(int i=0;i<=9;i++)
        ans+=1;
        
    //前len-1位
    for(int i=2;i<=len-1;i++)
    {
        for(int j=1;j<=9;j++)
        {
            ans+=dp[i][j];
        }
    }
    
    //第len位 且j<a[len]
    for(int i=1;i<=a[len]-1;i++)
    {
        ans+=dp[len][i];
        //printf("%d\n",ans);
    }

    //第len位 且j=a[len]
    for(int i=len-1;i>=1;i--)
    {
        for(int j=0;j<=a[i]-1;j++)
        {
            if(abs(j-a[i+1])>=2) ans+=dp[i][j];
        }
        if(abs(a[i+1]-a[i])<2) break;
    }
    return ans;
}

int main()
{
    ll a,b;
    scanf("%lld %lld",&a,&b);
    init();
    printf("%d\n",work(b+1)-work(a));
}

思维dp

乌龟棋

题目链接:https://ac.nowcoder.com/acm/problem/16590

根据题意 卡片最多只有4种 分别是走1 走2 走3 走4 那么可以定义dp[i][j][k][l]为走1 走2 走3 走4的卡片数 然后暴力把所有情况枚举出来

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
 
#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 360;
const int maxm = 105;
const int mod = 1000007;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
 
int a[maxn],dp[45][45][45][45],b[5];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for(int i=1; i<=m; i++)
    {
        int p;
        scanf("%d",&p);
        b[p]++;
    }
    dp[0][0][0][0]=a[1];
    for(int i=0; i<=b[1]; i++)
        for(int j=0; j<=b[2]; j++)
            for(int k=0; k<=b[3]; k++)
                for(int l=0; l<=b[4]; l++)
                {
                    int num=a[1+i+2*j+3*k+4*l];
                    if(i)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][j][k][l]+num);
                    if(j)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j-1][k][l]+num);
                    if(k)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k-1][l]+num);
                    if(l)
                        dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k][l-1]+num);
                }
    printf("%d\n",dp[b[1]][b[2]][b[3]][b[4]]);
}

摆花

题目链接:https://ac.nowcoder.com/acm/problem/16576

首先我们可以确定dp[i][j]:前i种花 摆j个位置的方案数 但是每一种花有a[i]个可以放 所以我们可以用一个循环枚举每次放i的个数 从0~a[i] 然后i-1就是从j-0~j-a[i] 最后注意初始化

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
 
#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 105;
const int maxm = 105;
const int mod = 1000007;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int a[maxn],dp[maxn][maxn];
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for(int i=0; i<=a[1]; i++)
        dp[1][i]=1;
    for(int i=2; i<=n; i++)
    {
        for(int j=0; j<=m; j++)
        {
            for(int k=0; k<=a[i]; k++)
            {
                if(k<=j)
                    dp[i][j]=(dp[i][j]+dp[i-1][j-k])%mod;
            }
        }
    }
    printf("%d\n",dp[n][m]%mod);
}

花匠

题目链接:https://ac.nowcoder.com/acm/problem/16535

题目要求的是两种 第一种是先升后降 第二种是先降后升 所以我们可以定义dp[i][j]:当j==0时 表示当前递增 j==1时 表示当前递减 然后就可以得出状态转移方程:

if(a[i]>a[i-1]) dp[i][0]=dp[i-1][1]+1;
else dp[i][0]=dp[i-1][0];
if(a[i]<a[i-1]) dp[i][1]=dp[i-1][0]+1;
else dp[i][1]=dp[i-1][1];

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
 
#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 1e5+10;
const int maxm = 105;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int a[maxn],dp[maxn][2];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    dp[1][0]=dp[1][1]=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>a[i-1]) dp[i][0]=dp[i-1][1]+1;
        else dp[i][0]=dp[i-1][0];
        if(a[i]<a[i-1]) dp[i][1]=dp[i-1][0]+1;
        else dp[i][1]=dp[i-1][1];
    }
    printf("%d\n",max(dp[n][0],dp[n][1]));
}

被3整除的子序列

题目链接:https://ac.nowcoder.com/acm/problem/21302

dp[i][j]表示前i个数和为j的总方案数 处理总方案数 然后j%3取和就是答案了

然后方程就出来了

但是这里我一开始对于j>=temp的情况没搞太清楚,我一开始推出的方程是dp[i][j]=(dp[i-1][j-temp]+dp[i-1][temp]+dp[i-1][j]+dp[i][j])%mod; 但是其实我们可以发现dp[i-1][temp]已经包括在dp[i-1][j]中了

/*Today you do things people will not do,
tomorrow you will do things people can not do.*/
 
#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 55;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const double PI = acos(-1.0);
const double eps = 1e-6;
ll dp[maxn][455];
char s[maxn];
int n[maxn];
ll ans=0;
int sum[maxn];
int main()
{
    scanf("%s",s);
    int len=strlen(s);
    for(int i=1;i<=len;i++)
    {
        n[i]=s[i-1]-'0';
        dp[i][n[i]]=1;
        sum[i]=n[i]+sum[i-1];
    }
    for(int i=1;i<=len;i++)
    {
        int temp=n[i];
        for(int j=0;j<=sum[i];j++)
        {
            if(j<temp) dp[i][j]=(dp[i-1][j]+dp[i][j])%mod;
            else dp[i][j]=(dp[i-1][j-temp]+dp[i-1][j]+dp[i][j])%mod;
        }
    }
    for(int i=0;i<=sum[len];i++)
    {
        if(i%3==0)
            ans=(ans+dp[len][i])%mod;
    }
    printf("%lld\n",ans);
}

删除子串

题目链接:https://ac.nowcoder.com/acm/problem/15362

这一题关键就在对上个串的尾部元素状态的记录,我们知道假设已经取了i个串,那么我们的决策有三种,第一种取了,然后变化加1长度加1,第二种不取,保持原来的状态。但是这两种都是要建立在我们要加入的字符和我们已经构造的字符的尾部元素不同时,所以我认为这题的难点就是记录尾部元素的状态。第三种尾部元素和加入字符相同,直接插入长度加1即可。 

接下来就是维护一个数组,这个数组记录了所有的dp[i][j]尾部元素的状态,当我们删除了加入的字符,状态不变,当我们加入了字符,那么状态转移成加入的字符。

/*Today you do things people will not do,
tomorrow you will do things people can not do.*/
 
#include<bits/stdc++.h>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
char s1[maxn];
int dp[maxn][15];
int n,m;
int cnt[maxn],pre[maxn][15];
int main()
{
    int flag=0,c1=0,c2=0;
    scanf("%d %d",&n,&m);
    getchar();
    for(int i=1; i<=n; i++)
        scanf("%c",&s1[i]);
    int cut=0;
    while(s1[cut+1]=='b')
        cut++;
    cut=cut+1;
    for(int i=cut; i<=n; i++)
    {
        if(s1[i]=='a')
            flag=1,c1++;
         dp[i][0]=c1;
    }
    if(m==0)
    {
        printf("%d\n",cnt[n]);
        return 0;
    }
    if(flag==0)
        puts("0");
    else
    {
        for(int i=1; i<=m; i++)
            dp[cut][i]=1;
        pre[cut][0]='a';
        for(int i=cut+1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                if(s1[i]!=pre[i-1][j])
                {
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+1);
                    if(dp[i][j]==dp[i-1][j]) pre[i][j]=pre[i-1][j];
                    if(dp[i][j]==dp[i-1][j-1]+1)
                        pre[i][j]=s1[i];
                }
                else
                    dp[i][j]=dp[i-1][j]+1,pre[i][j]=s1[i];
            }
        }
        printf("%d\n",dp[n][m]);
    }
}

  

转载于:https://www.cnblogs.com/CSUSTJCC/p/11318283.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值