dp动态规划(持续连载)

今天开始学习动态规划了呢/2020/4/3

动态规划真是简单呢(战术麻痹自己)

一、数字三角形/2020/4/3

P1216 [USACO1.5][IOI1994]数字三角形 Number Triangles
应该是最简单的dp题目了,从下往上滚
dp[i][j]等于脚下两个dp的最大值加上a[i][j]

#include <bits/stdc++.h>

using namespace std;
int w[500],c[500],dp[1005][1005],flag[500];
int tong[500];
int v,n;
int a[1005][1005];

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

二、最长上升子序列问题/2020/4/3

描述:
比如
1,7,3,5,8,4,8
问你最长的递增的子序列的长度是多少
这个问题的最优解有多个,但是最优值只有一个:长度为4
n^2算法
算法描述:dp[x]代表以x为尾的最长子序列的长度,那么dp[x+1]的值便可由dp[x]推来dp[x+1]的方程详见代码注释
蒻蒟的感悟:分析问题要用递归的思维去想,就是找一个实例,看这个dp与它的上一状态dp的关系
要明确dp所代表的的意义,比如这个题就是:表示以下标代表的数字为尾的子序列长度

#include <bits/stdc++.h>

using namespace std;
int a[1000],dp[1000];//dp表示以x为尾的最长子序列的长度
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    dp[1]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            //当a[i]大于a[j]时,那么a[i]便大于dp[j]代表的子序列的最大值
            //那么a[i]加上dp[j]代表的子序列的长度便是dp[j]+1
            //所以dp[i]就是这些长度(1加上子序列长度)的最大的那一个
            if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
        }
    }
    int ma=dp[1];
    //因为选的是最长,以最后一个数字为尾的子序列长度并不一定是最长的,所以求出以任意数字结尾的子序列长度最大的那一个
    for(int i=1;i<=n;i++)
    ma=max(ma,dp[i]);
    cout<<ma<<endl;
    return 0;
}

三、最长公共子序列问题(LCS)/2020/4/4

问题概述:给你两个序列,问你他们的最长LCS序列的长度是多少?(序列可以是不连续的,只要元素的相对位置一样)(不了解LCS问题的自行百度)

太开心了,这道题没看题解自己就做出来了,第一道完全自己推出状态转移方程的dp题,我tql,给自己orz(一眼就会做的dalao见笑了)

蒻蒟的感悟:先画一张(n+1)*(m+1)的表,更好找出规律,写出方程后也好验证

思路:dp[i][j]表示两个串前n,m个字符的最长公共序列,那么n+1 或m+1时公共序列长度也必定大于n,m的,所以继承二者最大的一个,然后当a[i]==a[j]时,公共长度+1(详见代码)

#include <bits/stdc++.h>

using namespace std;
int dp[1000][1005];//dp表示以x为尾的最长子序列的长度
string a,b;
int main()
{
    int n,m;
    cin>>n>>m;
    cin>>a>>b;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        if(a[i]==b[j])dp[i][j]++;
    }
    cout<<dp[n][m]<<endl;
    return 0;
}

四、矩阵连乘问题/2020/4/4

问题描述
给定n个矩阵:A1,A2,…,An,其中Ai与Ai+1是可乘的,i=1,2…,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。
算法描述
下标表示第几个矩阵
dp[i][j]表示把第i个矩阵到第j个矩阵相乘后的数乘次数
最外层循环控制矩阵长度1,2,3
中层循环推进矩阵相乘,dp[1][2],dp[2][3]
内层循环枚举两个矩阵相乘时的分割点,取其最小值

#include <bits/stdc++.h>

using namespace std;
#define INF 99999999
int dp[1000][1005];
int a[1005];
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<=n;i++)cin>>a[i];
    for(int len=1;len<=n;len++)//
        for(int i=1,j=i+len;j<=n;j++,i++)
    {
        int mi=INF;
        for(int k=i;k<j;k++)
        {
            //两个矩阵合并时新矩阵的数乘次数
            dp[i][j]=dp[i][k]+dp[k+1][j]+a[i-1]*a[k]*a[j];
            mi=min(mi,dp[i][j]);
        }
        dp[i][j]=mi;
    }
    cout<<dp[1][n]<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值