定义状态(定义子问题)
dp[i]:表示以 nums[i] 结尾 的 连续 子数组的最大和。
说明:「结尾」和「连续」是关键字。
状态转移方程(描述子问题之间的联系)
根据状态的定义,由于 nums[i] 一定会被选取,并且以 nums[i] 结尾的连续子数组与以 nums[i - 1] 结尾的连续子数组只相差一个元素 nums[i] 。
假设数组 nums 的值全都严格大于 0,那么一定有 dp[i] = dp[i - 1] + nums[i]。
可是 dp[i - 1] 有可能是负数,于是分类讨论:
如果 dp[i - 1] > 0,那么可以把 nums[i] 直接接在 dp[i - 1] 表示的那个数组的后面,得到和更大的连续子数组;
如果 dp[i - 1] <= 0,那么 nums[i] 加上前面的数 dp[i - 1] 以后值不会变大。于是 dp[i] 「另起炉灶」,此时单独的一个 nums[i] 的值,就是 dp[i]。
以上两种情况的最大值就是 dp[i] 的值,写出如下状态转移方程:

记为「状态转移方程 1」。
状态转移方程还可以这样写,反正求的是最大值,也不用分类讨论了,就这两种情况,取最大即可,因此还可以写出状态转移方程如下:

记为「状态转移方程 2」。
友情提示:求解动态规划的问题经常要分类讨论,这是因为动态规划的问题本来就有「最优子结构」的特点,即大问题的最优解通常由小问题的最优解得到。因此我们在设计子问题的时候,就需要把求解出所有子问题的结果,进而选出原问题的最优解。
思考初始值
dp[0] 根据定义,只有 1 个数,一定以 nums[0] 结尾,因此 dp[0] = nums[0]。
思考输出
注意:
这里状态的定义不是题目中的问题的定义,不能直接将最后一个状态返回回去。
简单的动态规划问题,很有可能问的问题就可以设计成为子问题,复杂的动态规划问题就没有那么容易看出子问题应该如何设计了,这需要一定的解决问题的经验。
这个问题的输出是把所有的 dp[0]、dp[1]、……、dp[n - 1] 都看一遍,取最大值。
根据「状态转移方程」,dp[i] 的值只和 dp[i - 1] 有关,因此可以使用「滚动变量」的方式将代码进行优化。
附上解题代码
public class Main {
/**
* 给一个整数数组 nums ,找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和
* @param args
*/
public static void main(String[] args) {
int[] ints = {-2,1,-3,4,-1,2,1,-5,4};
System.out.println(maxSubArray1(ints));
System.out.println(maxSubArray2(ints));
System.out.println(maxSubArray3(ints));
}
/**
* @param nums
* @return
*/
//动态规划状态转移方程1解法
private static int maxSubArray1(int[] nums) {
int len = nums.length;
// dp[i] 表示:以 nums[i] 结尾的连续子数组的最大和
int[] dp = new int[len];
dp[0] = nums[0];
int res = nums[0];
for (int i = 1; i < len; i++) {
if (dp[i - 1] > 0) {
dp[i] = dp[i - 1] + nums[i];
} else {
dp[i] = nums[i];
}
res = Math.max(res,dp[i]);
}
return res;
}
/**
* 动态规划状态转移方程2解法(动态规划转移方程1优化版)
* @param nums
* @return
*/
private static int maxSubArray2(int[] nums) {
int pre = 0;
int res = nums[0];
for (int num : nums) {
pre = Math.max(pre + num, num);
res = Math.max(res, pre);
}
return res;
}
/**
* 贪心算法解法(时间复杂度和空间复杂度最低)
* @param nums
* @return
*/
private static int maxSubArray3(int[] nums) {
int res = nums[0];
int sum = 0;
for (int num : nums) {
if (sum > 0) {
sum += num;
} else {
sum = num;
}
res = Math.max(res, sum);
}
return res;
}
}