本文目录
leetcode 21天动态规划入门——从0到0.5【Day05】最大数组和 与 转着圈的最大数组和 dp思想由浅入深
写在前面
今天是动态规划入门的第五天了,今早打开了leetcode发现发出的讨论引起了很多人的共鸣,这也不乏是对博主的鼓励,博主也会继续加油哒,那么话不多说,来,上菜~看看今天是什么题…
题目
- 最大子数组和 难度系数:**
题目一
给你一个整数数组 nums ,
请你找出一个具有最大和的连续子数组
(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例
示例1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例2:
输入:nums = [1]
输出:1
示例3:
输入:nums = [5,4,-1,7,8]
输出:23
提示
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
思路
老样子,自然是动态规划的题型 维护一个dp数组
和一个滚动变量res来获取当前数组的最大子数组和
再来用小部分推导全过程,动态规划的思路基本都大体相同
那么该题的动态转化方程我们可以根据示例来进行设计:
dp[0] = nums[0];
dp[1] = Math.max(dp[0]+nums[1],nums[1]);
此时的子数组和为:
-
当前一个子数组和与当前数值相加
-
当前数值
二者进行取最大值,获取得到当前的子数组和
dp[i] = Math.max(dp[i-1]+nums[i],nums[i])
然后我们利用一个滚动变量res 来记录最大的子数组和即:
res = Math.max(res,dp[i])
代码实现
class Solution {
public int maxSubArray(int[] nums) {
//特殊情况特殊处理
if (nums.length < 2 )return nums[0];
//维护一个dp数组用来记录此时的数组和
int[] dp = new int [nums.length+1];
int res = nums[0];
dp[0] = nums[0];
for (int i = 1;i< nums.length;i++){
//动态转化方程 因为如果前面的数是一个负数那肯定相加没有当前数值大
dp[i] = Math.max(nums[i],nums[i]+dp[i-1]);
res = Math.max(res,dp[i]);
}
return res;
}
}
执行结果
代码优化
用空间换时间的方法
同样是动态规划 动态转化方程也没有改
一看就懂~
class Solution {
public int maxSubArray(int[] nums) {
//维护当前的子数组和
int pre = 0;
int maxAns = nums[0];
for (int x : nums){
//如果当前加了一个负数 肯定 就保留当前的值
pre = Math.max(pre+x,x);
//滚动变量来求最大子数组和
maxAns = Math.max(maxAns,pre);
}
return maxAns;
}
}
题目二
- 环形子数组的最大和 难度系数:****
给定一个由整数数组 A 表示的环形数组 C,
求 C 的非空子数组的最大可能和。
在此处,环形数组意味着数组的末端将会与开头相连呈环状。
(形式上,当0 <= i < A.length 时 C[i] = A[i],
且当 i >= 0 时 C[i+A.length] = C[i])
此外,子数组最多只能包含固定缓冲区 A 中的每个元素一次。
(形式上,对于子数组 C[i], C[i+1], ..., C[j],
不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length)
示例
示例1:
输入:[1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
示例2:
输入:[5,-3,5]
输出:10
解释:从子数组 [5,5]
得到最大和 5 + 5 = 10
示例3:
输入:[3,-1,2,-1]
输出:4
解释:从子数组 [2,-1,3]
得到最大和 2 + (-1) + 3 = 4
示例4:
输入:[3,-2,2,-3]
输出:3
解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
示例5:
输入:[-2,-3,-1]
输出:-1
解释:从子数组 [-1] 得到最大和 -1
提示
-30000 <= A[i] <= 30000
1 <= A.length <= 30000
思路
本题有两种情况
1)当不需要循环访问时,此时题就跟上面那道题一样一样的,
求到最大值就是最长子数组和的值。
2)当需要循环访问时,且如题可知,每个数最多访问一次,
也就是说当再次到达起始位置之前,一定可以找到最大子数组和,
那就需要考虑,你会明白一个道理,
因为每个数最多只能访问一次,我们就假如全部进行访问,
那获取总值之后,找到了最小的子数组之和,
与之相减,那么获得的就是可循环访问的最大子数组之和
上述两种情况中找到最大值,就是此题的解。
代码实现
class Solution {
public int maxSubarraySumCircular(int[] nums) {
if(nums.length == 1)return nums[0];
//sum是用来记录总的数组和剩下的变量和第一题意义相同
int sum = 0,maxAns=nums[0],minAns=nums[0],pre=0;
//进行遍历找到无需循环时的最大子数组和
for (int i:nums){
sum += i;
pre = Math.max(i+pre,i);
maxAns = Math.max(pre,maxAns);
}
//置0
pre = 0 ;
//此次寻找最小的子数组之和
for (int i = 0; i<nums.length-1;i++){
pre = Math.min(nums[i]+pre,0);
minAns = Math.min(pre,minAns);
}
//比较获取最大子数组和
return Math.max(sum-minAns,maxAns);
}
}
执行结果
写在最后
Day05打卡成功!
今天的动态规划问题较昨日有所改变
不仅需要求出此时的动态转化方程还需要设置滚动变量来进行答案求解
而第二道题更能考察做题者的分类讨论思想,以及基本的数学功底
今天又是有所收获的一天呢~
最后
每天进步点 每天收获点
愿诸君 事业有成 学有所获
如果觉得不错 别忘啦一键三连哦~