关于动态规划

一、动态规划的三大步骤

动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维数组或者二维数组来保存。下面我们先来讲下做动态规划题很重要的三个步骤,

如果你听不懂,也没关系,下面会有很多例题讲解,估计你就懂了。之所以不配合例题来讲这些步骤,也是为了怕你们脑袋乱了

第一步骤:定义数组元素的含义,上面说了,我们会用一个数组,来保存历史数组,假设用一维数组 dp[] 吧。这个时候有一个非常非常重要的点,就是规定你这个数组元素的含义,例如你的 dp[i] 是代表什么意思?

第二步骤:找出数组元素之间的关系式,我觉得动态规划,还是有一点类似于我们高中学习时的归纳法的,当我们要计算 dp[n] 时,是可以利用 dp[n-1],dp[n-2]…..dp[1],来推出 dp[n] 的,也就是可以利用历史数据来推出新的元素值,所以我们要找出数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],这个就是他们的关系式了。而这一步,也是最难的一步,后面我会讲几种类型的题来说。

学过动态规划的可能都经常听到最优子结构,把大的问题拆分成小的问题,说时候,最开始的时候,我是对最优子结构一梦懵逼的。估计你们也听多了,所以这一次,我将换一种形式来讲,不再是各种子问题,各种最优子结构。所以大佬可别喷我再乱讲,因为我说了,这是我自己平时做题的套路。

第三步骤:找出初始值。学过数学归纳法的都知道,虽然我们知道了数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],我们可以通过 dp[n-1] 和 dp[n-2] 来计算 dp[n],但是,我们得知道初始值啊,例如一直推下去的话,会由 dp[3] = dp[2] + dp[1]。而 dp[2] 和 dp[1] 是不能再分解的了,所以我们必须要能够直接获得 dp[2] 和 dp[1] 的值,而这,就是所谓的初始值

由了初始值,并且有了数组元素之间的关系式,那么我们就可以得到 dp[n] 的值了,而 dp[n] 的含义是由你来定义的,你想求什么,就定义它是什么,这样,这道题也就解出来了。

下面我们写3道题:

公共子串计算

        公共子串,它的特点是必须是字串的元素必须是连续的  

步骤一: `dp[i][j]`表示以`text1[i-1]`和`text2[j-1]`结尾的最大公共子串的长度。

                如果两个字符不匹配,那么以这两个字符结尾的最大公共子串长度为`0`,因为子串必须是连续的。

步骤二 找出关系  分两种情况 :

                             

1  当text1[i] == text2[j]时
    dp[i][j] = dp[i-1][j-1]+1
    以text1[i]和text2[j]结尾的字串的长度,比以text1[i-1]和text2[j-1]的字串长度大1
       

2   当text[i] != text2[j]时
    dp[i][j] = 0;
     因为字串必须是连续的,所以有一个元素不等,那就不算匹配


        ans = max(f[i][j],ans);
        因为最优的值不一定是dp[text1.size()][text2.size()]  即不一定都是结尾匹配的情况
         

步骤三  设置初始条件

    
        dp[i][0] = 0            i = 0~text1.size(); 
        dp[0][j] = 0            j = 0~text2.size();
        显然一个元素为0的串,是没有最长字串的
            或者说,最长字串为0 

下面是代码

#include <iostream>
using namespace std;
string s, p;
int main() {
    cin >> s;
    cin >> p;
    int n = s.size();
    int m = p.size();

    int f[1005][1005];
    for (int i = 0 ; i <= n ; i++)
        f[i][0] = 0;
    for (int i = 0 ; i <= m ; i++)
        f[0][i] = 0;

        int maxx = -1;
    for (int i = 1 ; i <= n ; i++)
        for (int j = 1 ; j <= m ; j++) {
            if (s[i - 1] == p[j - 1])
                f[i][j] = f[i - 1][j - 1] + 1;
            else
                f[i][j] = 0;
                maxx = max(maxx,f[i][j]);
        }
       
         cout<<maxx;
}
// 64 位输出请用 printf("%lld")

最长上升子序列

        最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。

        它的特点是,可以不连续

步骤一     f[i] 定义为,以a[i]为结尾的最长上升序列的长度。

步骤二  找出关系

                

1 
    i = 2~n; j = 1 ~ i;

        j <= i
    
    当 a[i] > a[j] 时,证明a[j]是一个可选为上升序列的元素
        f[i] = max(f[i],f[j]+1)
  

2  
    当 a[i] == a[j] 时,没必要选a[j]
        f[i] = f[i];

3 
    当 a[i] < a[j] 时,显然不满足上升序列的条件
        不考虑


ans = max(ans,f[i])
    

步骤三  初始条件

i = 1 ~ n 
f[i] = 1

每一个元素都是长度为1的上升子序列

最终代码

#include <iostream>
using namespace std;
int n,a[5005];
int main() {

 

    int f[5005];
    cin>>n;
    
    for(int i = 1 ; i <= n ; i++)
    	cin>>a[i];
    	
    for (int i = 0 ; i <= n ; i++)
        f[i]=1;
        

int maxx = -1;
    for (int i = 2 ; i <= n ; i++)
        for (int j = 1 ; j < i ; j++) {
            if (a[j] < a[i])
            	f[i] = max(f[j]+1,f[i]);
            
            	
            	maxx = max(maxx,f[i]);
        }
       
         cout<<maxx;
}
// 64 位输出请用 printf("%lld")

最长公共子序列

           一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。       

        所以最长公共子序列不一定要连续

步骤一

        定义f[i][j]  是 str1 前 i-1 个字符。和 str2 前 j-1 个字符的最长公共子序列

步骤二

        找出关系

        

 i = 1~str1.size()
 j = 1~str2.size()

1
    当str1[i-1] == str2[j-1] 
    f[i][j] = f[i-1][j-1]+1
    最长子序列+1

2   当str1[i-1] != str2[j-1] 时
    f[i][j] = max(f[i][j],f[i-1][j]) 
    可以考虑删除str1[i],只考虑str1 前 i-1个字符

3   当str1[i-1] != str2[j-1] 时
    f[i][j] = max(f[i][j],f[i][j-1]) 
    可以考虑删除str2[j],只考虑str2 前 j-1个字符

4   当str1[i-1] != str2[j-1] 时
    f[i][j] = max(f[i][j],f[i-1][j-1]) 
    可以只考虑str1 前 i-1个字符 , 和str2 前 j-1个字符

ans = f[str1.size()][str2.size()]
    即综合考虑str1和str2所有的字符后的答案

步骤三 初始条件

        

 i = 1~str1.size()
 j = 1~str2.size()

    f[i][0] = 0;
    f[0][j] = 0;

    显然当一个序列元素个数为0时,其不可能与其他序列有公共序列

代码 

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int n = text1.size();
        int m = text2.size();

        int f[1005][1005];
        for(int i = 0 ; i <= n ; i++)
            f[i][0] = 0;
        for(int i = 0 ; i <= m ; i++)
            f[0][i] = 0;

    for(int i = 1 ; i <= n ; i++)
        for(int j = 1 ; j <= m ; j++)
        {
            if(text1[i-1] == text2[j-1])
                f[i][j] = f[i-1][j-1] + 1;
            else 
            f[i][j] = max(f[i-1][j],max(f[i-1][j-1],f[i][j-1]));
        }
        return f[n][m];
    }
    
};

  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值