数据结构与算法-进阶(二十一)动态规划案例

摘要

上期介绍动态规划的什么,有什么特性,能解决什么类型的问题。这期就通过一个案例,看一下动态规划解决问题的过程。

动态规划解决问题,需要做好这三步,1 定义状态,2 梳理状态转换方程,3 确定状态初始状态,最后得出的结果值。

接下来通过求最多子序列和这个案例,来体会一下。

最多子序列和

给定一个长度为 n 的整数序列,求它的最大连续子序列和,比如 -2、1、-3、4、-1、2、1、-5、4 的最大连续子序列和是 4 + (-1) + 2 + 1 =6。

首先定义状态,假设 dp(i) 是以 nums[i] 结尾的最大子序列和(nums 是整个序列):

  • nums[0] = -2 结尾的最大子序列是 -2,所以 dp(0) = -2;
  • nums[1] = 1 结尾的最大子序列是 1,所以 dp(1) = 1;
  • nums[2] = -3 结尾的最大子序列是 1、-3,所以 dp(2) = dp(1) -3 = -2;
  • nums[3] = 4 结尾的最大子序列是 4,所以 dp(3) = 4;
  • nums[4] = -1 结尾的最大子序列是 4、-1,所以 dp(4) = dp(3) - 1 = 3;
  • nums[5] = 2 结尾的最大子序列是 4、-1、2,所以 dp(5) = dp(4) + 2 = 5;
  • nums[6] = 1 结尾的最大子序列是 4、-1、2、1,所以 dp(6) = dp(5) + 1 = 6;
  • nums[7] = -5 结尾的最大子序列是 4、-1、2、1、-5,所以 dp(7) = dp(6) - 5 = 1;
  • nums[8] = 5 结尾的最大子序列是 4、-1、2、1、-5、4,所以 dp(8) = dp(7) + 4 = 5;

注意:
定义状态部分中“以 xx 结尾”,就是这个连续子序列必须以 xx 结尾。

然后梳理总结出状态转移方程,即总结出规律:

  • 如果 dp(i-1) ≤ \leq 0,那么 dp(i) = nums[i]
  • 如果 dp(i-1) > 0, 那么 dp(i) = dp(i-1) + nums[i]

然后确定初始状态dp(0) = nums[0]

最后,就可以得出最大子序列和是所以 dp(i) 中的最大值 max { dp(i) }, i ∈ \in [0, nums.length]。

实现代码

按照上面的梳理,实现代码如下:

static int maxSubArray1(int[] nums) {
    if (nums == null || nums.length == 0) return 0;
    int[] dp = new int[nums.length];
    dp[0] = nums[0];
    int max = dp[0];
    for (int i = 1; i < nums.length; i++) {
        int prev = dp[i-1];
        if (prev <= 0) {
            dp[i] = nums[i];
        } else {
            dp[i] = prev + nums[i];
        }
        max = Math.max(dp[i], max);
    }
    return max;
}

实现代码的空间复杂度和时间复杂度都是 O(n)。

看代码逻辑,每次 dp(i) 的处理只和 dp(i-1) 有关,那么就可以存在一个变量记录 dp(i-1) 的值,不需要一个 dp 数组去处理。优化代码如下:

static int maxSubArray(int[] nums) {
    if (nums == null || nums.length == 0) return 0;
    int dp = nums[0];
    int max = dp;
    for (int i = 1; i < nums.length; i++) {
        int prev = dp;
        if (prev <= 0) {
            dp = nums[i];
        } else {
            dp = prev + nums[i];
        }
        max = Math.max(dp, max);
    }
    return max;
}

优化后的时间复杂度是 O(n),但是空间复杂度降为 O(1) 了。

总结

  1. 定义状态

  2. 梳理状态转换方程

  3. 确定状态初始状态

这就是动态规划的正确解决套路。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我为双鱼狂

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

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

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

打赏作者

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

抵扣说明:

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

余额充值