leetcode动态规划系列

本文详细探讨了LeetCode中涉及动态规划的算法题目,涵盖斐波那契数列、强盗抢劫、股票交易等多个场景,通过动态规划解决这些问题,降低时间复杂度,提高解题效率。
摘要由CSDN通过智能技术生成

递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算。

斐波那契数列

1. 爬楼梯

70. Climbing Stairs (Easy)

Leetcode / 力扣

题目描述:有 N 阶楼梯,每次可以上一阶或者两阶,求有多少种上楼梯的方法。

定义一个数组 dp 存储上楼梯的方法数(为了方便讨论,数组下标从 1 开始),dp[i] 表示走到第 i 个楼梯的方法数目。

第 i 个楼梯可以从第 i-1 和 i-2 个楼梯再走一步到达,走到第 i 个楼梯的方法数为走到第 i-1 和第 i-2 个楼梯的方法数之和。

d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] dp[i] = dp[i-1] + dp[i-2] dp[i]=dp[i1]+dp[i2]
考虑到 dp[i] 只与 dp[i - 1] 和 dp[i - 2] 有关,因此可以只用两个变量来存储 dp[i - 1] 和 dp[i - 2],使得原来的 O(N) 空间复杂度优化为 O(1) 复杂度。

class Solution {
   
public:
    int climbStairs(int n) {
   
        if(n < 2)  return n;
        vector<int> dp(n);
        dp[0] = 1; dp[1] = 2;
        for(int i=2; i<n; i++)
            dp[i] = dp[i-1] + dp[i-2];
        return dp.back();
    }
};

2. 强盗抢劫

198. House Robber (Easy)

Leetcode / 力扣

题目描述:抢劫一排住户,但是不能抢邻近的住户,求最大抢劫量。

定义 dp 数组用来存储最大的抢劫量,其中 dp[i] 表示抢到第 i 个住户时的最大抢劫量。

由于不能抢劫邻近住户,如果抢劫了第 i -1 个住户,那么就不能再抢劫第 i 个住户,所以

d p [ i ] = m a x ( d p [ i − 2 ] + n u m s [ i ] , d p [ i − 1 ] ) dp[i] = max(dp[i-2] + nums[i], dp[i-1]) dp[i]=max(dp[i2]+nums[i],dp[i1])

class Solution {
   
public:
    int rob(vector<int>& nums) {
   
        if(nums.size() <= 1) 
            return nums.empty() ? 0 : nums[0];
        vector<int> dp(nums.size(), 0);
        dp[0] = nums[0]; dp[1] = max(nums[0], nums[1]);
        for(int i=2; i<nums.size(); i++)
            dp[i] = max(nums[i]+dp[i-2], dp[i-1]);
        return dp[nums.size()-1];
    }
};

3. 强盗在环形街区抢劫

213. House Robber II (Medium)

Leetcode / 力扣

环状排列意味着第一个房子和最后一个房子中只能选择一个偷窃,因此可以把此环状排列房间问题约化为两个单排排列房间子问题:

1在不偷窃第一个房子的情况下(即 nums[1:]nums[1:]),最大金额是 p1 
2在不偷窃最后一个房子的情况下(即 nums[:n-1]nums[:n−1]),最大金额是 p2
综合偷窃最大金额: 为以上两种情况的较大值,即 max(p1,p2)
class Solution {
   
public:
    int rob(vector<int>& nums) {
   
        if(nums.size() <= 1) return nums.empty() ? 0 : nums[0];
        return max(robRange(nums, 0, nums.size()-1), robRange(nums, 1, nums.size()));
    }
    int robRange(vector<int>& nums, int left, int right){
   
        if(right - left <= 1) return nums[left];
        vector<int> dp(right, 0);
        dp[left] = nums[left]; dp[left+1] = max(nums[left], nums[left+1]);
        for(int i=left+2; i<right; i++)
            dp[i] = max(nums[i]+dp[i-2], dp[i-1]);
        return dp.back();
        
    }
};

4. 信件错排

题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。

定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况:

  • i==k,交换 i 和 j 的信后,它们的信和信封在正确的位置,但是其余 i-2 封信有 dp[i-2] 种错误装信的方式。由于 j 有 i-1 种取值,因此共有 (i-1)*dp[i-2] 种错误装信方式。
  • i != k,交换 i 和 j 的信后,第 i 个信和信封在正确的位置,其余 i-1 封信有 dp[i-1] 种错误装信方式。由于 j 有 i-1 种取值,因此共有 (i-1)*dp[i-1] 种错误装信方式。

综上所述,错误装信数量方式数量为:


5. 母牛生产

程序员代码面试指南-P181

题目描述:假设农场中成熟的母牛每年都会生 1 头小母牛,并且永远不会死。第一年有 1 只小母牛,从第二年开始,母牛开始生小母牛。每只小母牛 3 年之后成熟又可以生小母牛。给定整数 N,求 N 年后牛的数量。

第 i 年成熟的牛的数量为:


矩阵路径

1. 矩阵的最小路径和

64. Minimum Path Sum (Medium)

Leetcode / 力扣

[[1,3,1],
 [1,5,1],
 [4,2,1]]
Given the above grid map, return 7. Because the path 1→3→1→1→1 minimizes the sum.

题目描述:求从矩阵的左上角到右下角的最小路径和,每次只能向右和向下移动。

class Solution {
   
public:
    int minPathSum(vector<vector<int>>& grid) {
   
        int m = grid.size(), n = grid[0].size();
        for(int i=0; i<m; i++){
   
            for(int j=0; j<n; j++){
   
                if(i==0 && j>0)
                    grid[i][j] = grid[i][j] + grid[i][j-1];
                if(j==0 && i>0)
                    grid[i][j] = grid[i][j] + grid[i-1][j];
                if(i>0 && j>0)
                    grid[i][j] = grid[i][j] + min(grid[i-1][j], grid[i][j-1]); 
            }  
        }
        return grid[m-1][n-1];
    }
};

2. 矩阵的总路径数

62. Unique Paths (Medium)

Leetcode / 力扣

题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向右或者向下移动。
在这里插入图片描述

class Solution {
   
public:
    int uniquePaths(int m, int n) {
   
        int path[m][n];
        for(int i=0; i<m; i++){
   
            for(int j=0; j<n; j++){
   
                if(i==0 || j==0) path[i][j] = 1;
                else path[i][j] = path[i-1][j] + path[i][j-1];
            }
        }       
        return path[m-1][n-1];
    }
};

也可以直接用数学公式求解,这是一个组合问题。机器人总共移动的次数 S=m+n-2,向下移动的次数 D=m-1,那么问题可以看成从 S 中取出 D 个位置的组合数量,这个问题的解为 C(S, D)。

public int uniquePaths(int m, int n) {
   
    int S = m + n - 2;  // 总共的移动次数
    int D = m - 1;      // 向下的移动次数
    long ret = 1;
    for (int i = 1; i <= D; i++) {
   
        ret = ret * (S - D + i) / i;
    }
    return (int) ret;
}

数组区间

1. 数组区间和

303. Range Sum Query - Immutable (Easy)

Leetcode / 力扣

Given nums = [-2, 0, 3, -5, 2, -1]

sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3

求区间 i ~ j 的和,可以转换为 sum[j + 1] - sum[i],其中 sum[i] 为 0 ~ i - 1 的和。

class NumArray {
   
public:
    vector<int> dp;
    NumArray(vector<int>& nums) {
   
        dp = nums;
        for(int i=1; i<nums.size(<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值