动态规划|特殊的多行规划|dp[2][] 用两行元素分别记录状态变化

文章介绍了动态规划中处理多个状态变化值的问题,通过多行规划的思想解决376.摆动序列和152.乘积最大子数组这两道题目。对于摆动序列,使用两行DP分别记录以降序和升序结尾的最长序列。而对于最大乘积子数组,同样利用两行DP保存最大值和最小值,以应对正负数转换的情况。通过这种方式,可以简化复杂的一维或二维DP问题。
摘要由CSDN通过智能技术生成

多行规划是我自己整理此类问题时起的名字,如有专属名词,麻烦评论告知

用于处理当动态规划中,需要记录多个值的状态变化时。

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;

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值