动态规划2---线性dp&区间dp

本篇文章总结动态规划的第二大类问题,即线性dp问题和区间dp问题。

题目1:数字三角形

题目描述:

这道题目怎么想?我们还是从y总的dp分析法出发。

 在定义坐标的行和列时,我们如下的定义方式:

 这样的话就好理解了:

#include<bits/stdc++.h>
using namespace std;

const int INF=1e9;
int a[505][505];
int f[505][505];
int 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++)//这个范围要往后开一些,否则的话很可能就会导致计算错误
         f[i][j]=-INF;

   f[1][1]=a[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]+a[i][j],f[i-1][j]+a[i][j]);//状态转移方程求最大值
      }
    
    int res=-INF;
    
    for(int i=1;i<=n;i++)
       res=max(res,f[n][i]);
    
    cout<<res;
    return 0;
}

题目2:最长上升子序列

最长上升子序列问题是线性dp里面也是整个动态规划里面的经典问题,最长上升子序列的描述如下:

根据问题的描述不难发现是从n个数里面选出最长的子序列。选法其实有很多种,可以就只有一个数字组成,也可以是2个,3个数等等组成的序列。 

闫总的dp分析法。

代码如下:

#include<bits/stdc++.h>
using namespace std;

int n;
int a[1010];
int f[1010];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];//输入系列数字
    
    for(int i=1;i<=n;i++)
    {
        f[i]=1;//因为自己肯定算一个所以初始化f[i]一定是1
        for(int j=1;j<i;j++)//从当前数的前面去比较
          if(a[j]<a[i])//必须满足的条件
             f[i]=max(f[i],f[j]+1);//更新f[i]的值
    }
    
    int res=0;
    
    for(int i=1;i<=n;i++) res=max(res,f[i]);//找出再大的数
    cout<<res;
    return 0;
}

如果说,让输出最长子序列是什么,那中间再加上一个数组就可以了。

#include<bits/stdc++.h>
using namespace std;
int n;
int a[1005];
int f[1005];
int g[1005];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        g[i]=0;
        for(int j=1;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]<<endl;
    
    for(int i=0,len=f[k];i<len;i++)
    {
        cout<<a[k]<<" ";
        k=g[k];
    }
    return 0;
}

题目3:最长公共子序列

这类题目也是很常见的一类问题,题目的描述为:

这道题目如果简单的分析的话很容易出错,我们先看如何用dp分析法进行分析。

 代码如下:

#include<bits/stdc++.h>
using namespace std;

int f[1005][1005];
char a[1005],b[1005];
int n,m;

int main()
{
    cin>>n>>m;
    cin>>a+1>>b+1;//两个字符串要用字符型数组去存,如果用string的话下标是从0开始的,下面的代码会越界。
    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;
}

题目4:最短编辑距离

先看题目描述

 这道题目也是动态规划里面的典型的题目,我们用y式dp分析法来分析这道题目。

三种情况。第一种情况可以理解为,前i-1个和j个已经匹配,要删掉第i个才能整体匹配。第二种情况表示前i个和前j-1个已经匹配,要想整体匹配,必须加上一个。第三种情况是分为两种,当a[i]==b[j]的时候表示不需要操作,加上0即可,当a[i]!=b[j]的时候,再加上一步修改操作,所以再加上1。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,m;
char a[1005],b[1005];
int f[1005][1005];
int main()
{
    cin>>n; cin>>a+1;
    cin>>m; cin>>b+1;
    
    for(int i=0;i<=n;i++) f[i][0]=i;
    for(int i=0;i<=m;i++) f[0][i]=i;
    //这两行是比较难考虑到的,第一行表示前i个和0个匹配,就要删除a的i个
    //第二行表示a变成b要加上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-1][j-1],f[i][j]);//第三种情况
          else f[i][j]=min(f[i][j],f[i-1][j-1]+1);
      }
      
    cout<<f[n][m];
    return 0;
    
}

和这道题做法一样的一道题目

代码:

#include<bits/stdc++.h>
using namespace std;
char a[1005][1005];

int f[1005][1005];
int n,m;
int t;


int caozuo(char a[],char b[])//执行操作的函数,和上一题一样的代码
{
    int la=strlen(a+1);int 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);
          if(a[i]==b[j]) f[i][j]=min(f[i][j],f[i-1][j-1]);
          else f[i][j]=min(f[i][j],f[i-1][j-1]+1);
      }
    
    return f[la][lb];
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i]+1;//这里是第一次见到,学习字符串的存储
    
    while(m--)
    {
        char b[15];
        cin>>b+1>>t;
        int ans=0;
  
        for(int i=1;i<=n;i++)//遍历每一个
        {
            if(caozuo(a[i],b)<=t)
              ans++;
        }
        cout<<ans<<endl;
    }
    
    return 0;
}

区间dp

题目1:石子合并

先看题目描述:

这道题目我暂时没有听懂,先放在这,先贴入y总的分析和代码:

 

 代码:

#include <bits/stdc++.h>
using namespace std;
int n, dp[310][310], a[310], s[310];
int main() {
    scanf("%d", &n);
    for (int i = 1;i <= n; i++) scanf("%d", &a[i]), s[i] = s[i - 1] + a[i];

    memset(dp, 0, sizeof dp);
    for (int len = 2; len <= n; len++)
        for (int i = 1;i + len - 1 <= n; i++) {
            int j = i + len - 1; dp[i][j] = 0x3f3f3f3f;
            for (int k = i; k <= j; k++) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + s[j] - s[i - 1]);
        }
    printf("%d\n", dp[1][n]);
    return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值