从零备战蓝桥杯——动态规划(递推篇)


双非刷leetcode备战2023年蓝桥杯,qwq加油吧,无论结果如何总会有收获!一起加油,我是跟着英雄哥的那个思维导图刷leetcode的,大家也可以看看所有涉及到的题目用leetcode搜索就可以哦,因为避让添加外链,一起加油!!!

动态规划将分为五个板块来讲,本篇为基础dp篇都是基础题目适合入门
请添加图片描述



基础篇:

不会吧不会吧,你连dp的基础都不会,我不信我不信,反正我会(!我也不会,略懂一点点),这是一种思想吧,如果不会的话可以看我这几篇博文,一个是八皇后的,一个是递推的,我就不添加链接了哈,直接点头像就能看~话不多说你要是看完了咱就直接开始刷题 ,这其实就是一种思想,我认为直接刷题就可以了~所以咱直接刷题哈,因为题目会告诉咱啥叫动态规划~

五步走

    1. 确定dp数组下标含义
    1. 递推公式
    1. 初始化
    1. 遍历顺序
    1. 推导结果

当然不是所有的dp问题都这么简单的能找到


Leetcode相关题目

基础dp:

各种递推题目

如:509,70,746.各位可以刷刷

二维递推:62. 不同路径

思路:不会的看我的博文:4000字,让你明白递推及其例题做法(C语言,有图)点头像就有哈,里面有个马兰过河卒问题,一模一样的(没障碍这个)

class Solution {
public:
    int uniquePaths(int m, int n) {
    int a[m+1][n+1];
    for(int i=1;i<=m;i++)
    {
        a[i][1]=1;
    }
       for(int i=1;i<=n;i++)
    {
        a[1][i]=1;
    }
    //初始化
    for(int i=2;i<=m;i++)
    {
        for(int j=2;j<=n;j++)
        {
            a[i][j]=a[i-1][j]+a[i][j-1];
        }
    
    }
    return a[m][n];
    }
};

二维递推:63. 不同路径

思路:不会的看我的博文:4000字,让你明白递推及其例题做法(C语言,有图)点头像就有哈,里面有个马兰过河卒问题,一模一样的

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int n = obstacleGrid.size(), m = obstacleGrid.at(0).size();
        vector <int> f(m);

        f[0] = (obstacleGrid[0][0] == 0);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (obstacleGrid[i][j] == 1) {
                    f[j] = 0;
                    continue;
                }
                if (j - 1 >= 0 && obstacleGrid[i][j - 1] == 0) {
                    f[j] += f[j - 1];
                }
            }
        }

        return f.back();
    }
};

343. 整数拆分

思路:

class Solution {
public:
int integerBreak(int n) {
    return (vector<int>{ -1, -1, 1, 2, 4, 6, 9, 12, 18, 27, 36, 54, 81, 108, 162, 243, 324, 486, 729, 972, 1458, 2187, 2916, 4374, 6561, 8748, 13122, 19683, 26244, 39366, 59049, 78732, 118098, 177147, 236196, 354294, 531441, 708588, 1062882, 1594323, 2125764, 3188646, 4782969, 6377292, 9565938, 14348907, 19131876, 28697814, 43046721, 57395628, 86093442, 129140163, 172186884, 258280326, 387420489, 516560652, 774840978, 1162261467, 1549681956  })[n];
}
};

开个玩笑,下面是正八经的思路了:

  • 首先dp[i]表示的就是最大的乘积;
  • dp公式就是: dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
    • 这个咱dp[i]不就是乘积嘛,用这个乘积和 max((i - j) * j, dp[i - j] * j)作比较看看那个大哪个就是dp,那你就问了这个是啥,凭啥能表示
      • 将 i 拆分成 j 和 i-j 的和,且 i-j 不再拆分成多个正整数,此时的乘积是(i - j) * j
      • 将 i 拆分成 j 和 i-j 的和,且 i-j 继续拆分成多个正整数,此时的乘积是dp[i - j] * j
        怎么拆呢,答案就是用j拆,j可能从1到i-1的任何一个数用j来吧i拆成两个数j和i-j。要是还能拆就是j和dp[i-j];因为dp[i-j]肯定是i-j这个点最大的值,所以可以用dp[i-j]来表示这个是拆成多个时候的最大值,当然最大值×肯定是最大的。
        所以就做完了~!
class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n+1);
        dp[2] = 1;
        for (int i = 3; i <= n ; i++) {
            for (int j = 1; j < i - 1; j++) {
                dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
            }
        }
        return dp[n];
    }
};

96. 不同的二叉搜索树

咱讲真,dp的题目看完答案好简单不看答案啥也不会,真服了
初始值:dp[0]=1;dp[1]=1;
循环:
首先1~n中都可以做根节点, for (int i = 2; i <= n; i++)来表示让哪个作为根节点

如果令t为根节点的话那1 ~ t 就会作为左子树(树小)而t ~n个数就作为右子树了(数大)
那首先我们可以fo循环一下让1到n分别为根节点那dp(j-1)*dp(n-j)就是这个节点的个数了
在t点左子树可能有多少种情况?

  • 左 1 右 t-1。
  • 左 2 右 t-2。
  • 左t-1 右 1。

可以看出规律左边的是逐渐递增的怎么表示呢当然是循环了 for (int j = 1; j <= i;j++)

然后 G[i] += G[j - 1] * G[i - j];就可以表示这个为根时的搜索树有多少种结果

class Solution {
public:
    int numTrees(int n) {
        vector<int> G(n + 1);
        G[0] = 1;
        G[1] = 1;

        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i;j++) {
                G[i] += G[j - 1] * G[i - j];
            }
        }
        return G[n];
    }
};

卡塔兰数catalan

然后由这个结果我们可以得到一个公式
事实上我们在方法一中推导出的 G(n)函数的值在数学上被称为卡塔兰数
​卡塔兰数更便于计算的定义如下
在这里插入图片描述
然后这个题就可以这么做

class Solution {
public:
    int numTrees(int n) {
        long long C = 1;
        for (int i = 0; i < n; ++i) {
            C = C * 2 * (2 * i + 1) / (i + 2);
        }
        return (int)C;
    }
};

这个数还能解决什么问题呢?
在这里插入图片描述
复制的百度哈


​​在这里插入图片描述

Love is worth years.❤
热爱可抵岁月漫长。

​​​​
本文部分思路来源于网络如有侵权联系删除~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c0re

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值