动态规划问题 -- 子数组模型(乘积最大数组)
目录
- 动态规划分析问题五步曲
- 题目概述
- 代码编写
动态规划分析问题五步曲
不清楚动态规划分析问题是哪关键的五步的少年们可以移步到
链接: 动态规划算法基础
这篇文章非常详细的介绍了动态规划算法是如何分析和解决问题的
题目概述
链接: 乘积最大子数组
- 状态表示(题目要求+自己的经验)
根据惯例提出本题状态表示:dp[i]即表示以第i个位置为结尾的子数组最大值之乘积
不过根据题意,我们发现第nums[i]个数可能是正数,也可能是负数
如果是正数,一个尽可能大的子数组乘以正数就会得到另一个尽可能大的子数组这无可厚非
但是如果是负数呢,必须一个尽可能小的子数组乘以负数才能得到一个尽可能大的子数组!!!
所以要得到尽可能大的子数组必须得有两个状态 :
- correctDp[i] : 表示以第i个位置为结尾的乘积尽可能大的子数组
- loseDp[i] : 表示以第i个位置为结尾的乘积尽可能小的子数组
- 状态转移方程推导
第nums[i]个位置有三种情况 即 正数或负数或0
:若nums[i]为0,那么很简单了,如何数乘以0都等于0,根据状态表示此时correctDp[i] = loseDp[i] = 0
根据我们上面的状态表示分析
:若nums[i] > 0 , 一个尽可能大的子数组乘以正数就会得到另一个尽可能大的子数组
*************** ,一个尽可能小的子数组乘以正数就会得到另一个尽可能小的子数组
:若nums[i] < 0 , 一个尽可能小的子数组乘以负数就会得到另一个尽可能大的子数组
*************** ,一个尽可能大的子数组乘以负数就会得到另一个尽可能小的子数组
得出状态转移方程 :
- 初始化(防止越界+结合状态表示初始化)
根据状态转移方程,当i = 0时会发生越界
根据状态表示和题目要求 :
correctDp[0] = loseDp[0] = nums[0]即可
- 填表顺序(分析要填i位置前一个依赖状态的位置)
本题两个dp表显然都是从左到右填表
- 返回值(由题目要求来)
因为不确定乘积最大的子数组是以哪个位置结尾的,所以没for循环一次就记录当前的最大值
代码编写
有了动态规划五步曲我们就可以写出非常优雅的代码了
int maxProduct(vector<int>& nums) {int n = nums.size();if(n == 1) return nums[0];//一个记录乘积最小的子数组,一个记录乘积最大的子数组vector<int>correctDp(n);auto loseDp = correctDp;int correctMax = nums[0];loseDp[0] = correctDp[0] = nums[0];for(int i = 1 ; i < n ; i++){//如果为正数,那么肯定是乘积最大的子数组乘以正数是乘积最大的子数组// 乘积最小的子数组乘以正数是乘积最小的子数组if(nums[i] > 0){correctDp[i] = max(correctDp[i-1]*nums[i],nums[i]);loseDp[i] = min(loseDp[i-1]*nums[i],nums[i]);}//如果是负数,那么肯定是乘积最小的子数组乘以负数得到乘积最大的子数组// 乘积最大的子数组乘以负数得到乘积最小的子数组else if(nums[i] < 0){correctDp[i] = max(loseDp[i-1]*nums[i],nums[i]);loseDp[i] = min(correctDp[i-1]*nums[i],nums[i]);}else{correctDp[i] = loseDp[i] = 0;}correctMax = max(correctMax,max(correctDp[i],loseDp[i]));}return correctMax;}
少年,今天你又进步了一点点哟,明天继续加油吧