力扣53. 最大子数组和 + 918. 环形子数组的最大和
力扣53. 最大子数组和:【https://leetcode.cn/problems/maximum-subarray/description/】
经典的dp + 空间优化,代码如下
1 class Solution { 2 public: 3 int maxSubArray(vector<int>& nums) { 4 int n = nums.size(); 5 // vector<int> dp(n); 6 // dp[0] = nums[0]; 7 // int ret = dp[0]; 8 int dp = nums[0]; 9 int ret = dp; 10 for (int i = 1; i < n; ++i) { 11 // dp[i] = max(dp[i - 1] + nums[i], nums[i]); 12 dp = max(dp + nums[i], nums[i]); 13 ret = max(dp, ret); 14 // if (dp[i] > ret) 15 // ret = dp[i]; 16 } 17 return ret; 18 } 19 };
力扣918. 环形子数组的最大和:【https://leetcode.cn/problems/maximum-sum-circular-subarray/description/?envType=study-plan-v2&envId=top-interview-150】
这个题目在基础题的基础上,增加了环形的可能性,想部分情况讨论,应该是解不出来的。
情况1还是按照基础题的来。
情况2是跨环形了,如果跨环形的话,意味着0到i这些数字必须都用到,j到n的数字也必须都用到,不能说按照常规dp那样,可以丢弃一部分。
所以先用lsum统计总数值,可能lsum包含了一些“累赘”,但是仍然都要用到,另外max中用的是dp[i - 1]和lsum作比较,也就意味着,
①假如前三个是【5,-3,5】,后面到i都是负数,那么此时dp[i]记录的仍然是前三个数的和,一直递推到最后,注意此时无法优化空间,因为反向递推的适合,需要用到之前的数据。
后面从右向左递推,rsum统计总数值,这时候rsum + dp2[i - 1]如果大于之前的最大值,则rsum + dp2[i - 1]就是最优解,这里之所以从右开始加,加到i的位置的总数加上dp2的[i - 1]就是最值的原因如①的描述。
1 class Solution { 2 public: 3 int maxSubarraySumCircular(vector<int>& nums) { 4 int n = nums.size(); 5 vector<int> dp2(n); 6 dp2[0] = nums[0]; 7 int lsum = nums[0]; 8 9 int dp = nums[0]; 10 int ret = dp; 11 for (int i = 1; i < n; ++i) { 12 dp = max(dp + nums[i], nums[i]); 13 ret = max(dp, ret); 14 lsum += nums[i]; 15 dp2[i] = max(dp2[i - 1], lsum); 16 } 17 18 int rsum = 0; 19 for (int i = n - 1; i > 0; --i) { 20 rsum += nums[i]; 21 ret = max(ret, rsum + dp2[i - 1]); 22 } 23 return ret; 24 } 25 };
这个题目的dp方式是按照题解来的。
但是这个题目的最优解并不是dp,官方最终解法为,将情况二的正向思路改为反向思路,从而减少一轮遍历,从而让时间复杂度降低,最重要的是将空间复杂度降为常数。
思路是将情况二的【0到i】【j到n】的区间反向思维,求出连续最小值的和,然后用总的和减去这个数字,剩余的就是两端要求出来的最大和,由于最小和最大的求法一致,直接就减少了一轮遍历,并且空间都按照一个变量来算即可,减少了一个dp数组。
1 class Solution { 2 public: 3 int maxSubarraySumCircular(vector<int>& nums) { 4 int n = nums.size(); 5 int dp1 = nums[0], ret1 = dp1; 6 int dp2 = nums[0], ret2 = dp2; 7 int s = nums[0]; 8 9 for (int i = 1; i < n; ++i) { 10 dp1 = max(dp1 + nums[i], nums[i]); 11 ret1 = max(ret1, dp1); 12 dp2 = min(dp2 + nums[i], nums[i]); 13 ret2 = min(ret2, dp2); 14 s += nums[i]; 15 } 16 return s == ret2 ? ret1 : max(ret1, s - ret2); 17 } 18 };
只能说反向思维,无敌!
浙公网安备 33010602011771号