多行规划是我自己整理此类问题时起的名字,如有专属名词,麻烦评论告知
用于处理当动态规划中,需要记录多个值的状态变化时。
376. 摆动序列(特殊的自定义二维dp)
做惯了一般的动态规划,突然看到这种题目,容易发懵。
明明感觉是动态规划,但是你就是很难套进去,一维的做不出,二维的太复杂。
其实可以换一个思路,我们可以用两行dp分别记录状态的变化
dp[0][i] 到达i元素,以降序为结尾的 摆动序列 的最大长度
dp[1][i] 到达i元素,以升序为结尾的 摆动序列 的最大长度
然后每次遍历到i元素的时候,进行判断
若是升序,可拼凑到降序结尾的摆动序列,即之前降序为结尾的摆动序列最大长度+1,
dp[1][i] = dp[0][i-1] + 1; dp[0][i]则不变
同理 若是降序 dp[0][i] = dp[1][i-1]+1;dp[1][i]不变
若是相等,全不变
那么就很容易写出来了
class Solution {
public int wiggleMaxLength(int[] nums) {
// dp[0][i] 到达i元素,以降序为结尾的 摆动序列 的最大长度
// dp[1][i] 到达i元素,以升序为结尾的 摆动序列 的最大长度
// 到达i元素后,若是升序,可拼凑到降序结尾的摆动序列,即之前降序为结尾的摆动序列最大长度+1,则 dp[1][i] = dp[0][i-1] + 1; dp[0][i]则不变
// 同理 若是降序 dp[0][i] = dp[1][i-1]+1;dp[1][i]不变
// 若是相等,全不变
// 初始化,dp[0][0]=1, dp[0][1]=1;
int[][] dp = new int[2][nums.length];
dp[0][0]=1;
dp[1][0]=1;
for(int i=1;i<nums.length;i++){
// 若是升序
if(nums[i]>nums[i-1]){
dp[1][i] = dp[0][i-1] + 1;
dp[0][i] = dp[0][i-1];
}else if(nums[i]<nums[i-1]){
dp[0][i] = dp[1][i-1]+1;
dp[1][i] = dp[1][i-1];
}else{
dp[0][i] = dp[0][i-1];
dp[1][i] = dp[1][i-1];
}
}
return Math.max(dp[0][nums.length-1],dp[1][nums.length-1]);
}
}
当然,你会发现并不需要用数组存储,直接用两个变量存储即可。
class Solution {
public int wiggleMaxLength(int[] nums) {
int up=1;
int down=1;
for(int i=1;i<nums.length;i++){
// 若是升序
if(nums[i]>nums[i-1]){
up = down + 1;
}else if(nums[i]<nums[i-1]){
down=up+1;
}
}
return Math.max(down,up);
}
}
但是你只有理解了上面的数组存储,后面的两个变量直接存储才会清楚如何实现的。
152. 乘积最大子数组
这道题其实思考一下,它也是有两份状态变化的,你需要存储一个最大值最小值,因为后面是负数,最大值会成最小值,最小值有可能就成为了最大值。
那么就想到了多行规划。
dp[][i] 表示到达第i个元素时,数组的最连续最大乘积
dp[2][i] 第一行最大值,第二行最小值
那么当遍历到i元素时,最大值只可能有三种情况:
最大值乘以nums[i],最小值乘以nums[i],nums[i]比较一下即可。
dp[0][i] = max{dp[0][i-1]*nums[i],dp[1][i-1]*nums[i],nums[i]}
同理
dp[1][i] = min{p[0][i-1]*nums[i],dp[1][i-1]*nums[i],nums[i]}
那么代码也就迎刃而解了
class Solution {
public int maxProduct(int[] nums) {
int[][] dp = new int[2][nums.length];
int max=nums[0];
// 初始化
dp[0][0]=nums[0];
dp[1][0]=nums[0];
// i从1开始遍历
for(int i=1;i<nums.length;i++){
dp[0][i] = Math.max(dp[0][i-1]*nums[i],
Math.max(dp[1][i-1]*nums[i],nums[i]));
dp[1][i] = Math.min(dp[0][i-1]*nums[i],
Math.min(dp[1][i-1]*nums[i],nums[i]));
max = Math.max(dp[0][i],max);
}
return max;
}
}
再按照老套路,优化一下dp
class Solution {
public int maxProduct(int[] nums) {
int max=nums[0];
int min=nums[0];
int plus=nums[0];
// i从1开始遍历
for(int i=1;i<nums.length;i++){
// 记录个max,避免后续计算min时,max已被改变
int temp = max;
max = Math.max(max*nums[i],Math.max(min*nums[i],nums[i]));
min = Math.min(temp*nums[i],Math.min(min*nums[i],nums[i]));
plus = Math.max(max,plus);
}
return plus;
}
}