《五月集训》(第二十八天)——动态规划

前言

        欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
        今天是五月集训第二十八天:动态规划🔥🔥🔥🔥🔥

一、练习题目

        70. 爬楼梯
        53. 最大子数组和
        1706. 球会落何处
        1420. 生成数组

二、算法思路

  • 1、70. 爬楼梯:我们用 f(x)f(x) 表示爬到第 xx 级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以我们可以列出如下式子:
    f ( x ) = f ( x − 1 ) + f ( x − 2 ) f(x) = f(x - 1) + f(x - 2) f(x)=f(x1)+f(x2)
      它意味着爬到第 x x x级台阶的方案数是爬到第 x − 1 x - 1 x1级台阶的方案数和爬到第 x − 2 x - 2 x2级台阶的方案数的和。很好理解,因为每次只能爬1 级或2级,所以 f ( x ) f(x) f(x)只能从 f ( x − 1 ) f(x - 1) f(x1) f ( x − 2 ) f(x - 2) f(x2)转移过来,而这里要统计方案总数,我们就需要对这两项的贡献求和。
      以上是动态规划的转移方程,下面我们来讨论边界条件。我们是从第0级开始爬的,所以从第0级爬到第0级我们可以看作只有一种方案,即 f ( 0 ) = 1 f(0) = 1 f(0)=1;从第 0 级到第 1 级也只有一种方案,即爬一级, f ( 1 ) = 1 f(1) = 1 f(1)=1。这两个作为边界条件就可以继续向后推导出第n级的正确结果。
  • 2、53. 最大子数组和:动态规划转移方程如下:
    f ( i ) = m a x f ( i − 1 ) + n u m s [ i ] , n u m s [ i ] f(i)=max{f(i−1)+nums[i],nums[i]} f(i)=maxf(i1)+nums[i],nums[i]
  • 3、1706. 球会落何处:偷鸡了,模拟了一下,这道题用动态规划有点没看懂题解。
  • 4、1420. 生成数组:困难题还是有点难度啊~

三、源码剖析

// 70. 爬楼梯
class Solution {
public:
    int climbStairs(int n) {
        if (n <= 2) {
            return n;
        }
        int dp[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2; //(1)
        for(int i = 3; i <= n; ++i) {
            dp[i] = dp[i - 2] + dp[i - 1]; //(2)
        return dp[n];
    }
};
  • 1、考虑边界条件;
  • 2、动态规划的递推关系式。
// 53. 最大子数组和
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int dp[100001];
        dp[0] = nums[0];
        int ans = dp[0];
        for(int i = 1; i < nums.size(); ++i) {
            dp[i] = max(dp[i - 1] + nums[i], nums[i]); //(1)
            ans = max(ans, dp[i]);
        }
        return ans;
    }
};
  • 1、判断dp[i - 1]对加上后一项有没有增益的效果,如果是正向的,保留并加上后一项;如果加上dp[i - 1]还不如当前遍历的数值大,那就选择另起炉灶,变成当前的数值;
  • 2、把结果更新成最大值。
// 1706. 球会落何处
class Solution {
public:
    vector<int> findBall(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        vector<int> ans(n, -1);

        for(int i = 0; i < n; ++i) {
            int c = i; //(1)
            bool block = false;
            for (int j = 0; j < m; ++j) {
                int c_ = c;
                c += grid[j][c]; //(2)
                if(c < 0 || c >= n) {
                    block = true;
                    break;
                } //(3)
                if(grid[j][c_] == 1 && grid[j][c] == -1) {
                    block = true;
                    break;
                }
                if(grid[j][c_] == -1 && grid[j][c] == 1) {
                    block = true;
                    break;
                } //(4)
            }
            if(block) continue;
            ans[i] = c;
        }
        return ans;
    }
};
  • 1、球所在的初始列;
  • 2、把球移动一下,1的话右移, -1的话左移;
  • 3、判断是不是在左边缘和右边缘;
  • 4、这里是判断是不是在中间形成了一个V字型。
// 1420. 生成数组

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值