LC-1043. 分隔数组以得到最大和(动态规划)

1043. 分隔数组以得到最大和

难度中等168

给你一个整数数组 arr,请你将该数组分隔为长度 最多 为 k 的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。

返回将数组分隔变换后能够得到的元素最大和。本题所用到的测试用例会确保答案是一个 32 位整数。

示例 1:

输入:arr = [1,15,7,9,2,5,10], k = 3
输出:84
解释:数组变为 [15,15,15,9,10,10,10]

示例 2:

输入:arr = [1,4,1,5,7,3,6,1,9,9,3], k = 4
输出:83

示例 3:

输入:arr = [1], k = 1
输出:1

提示:

  • 1 <= arr.length <= 500
  • 0 <= arr[i] <= 109
  • 1 <= k <= arr.length

线性DP

class Solution {
    // 由于题目要求的是,子数组元素个数不大于k。那么跟arr[3]在一个分组的情况一共有k种
    /**
        dp[2]+max(arr[2])*1
        dp[1]+max(arr[2], arr[1])*2
        dp[0]+max(arr[2], arr[1], arr[0])*3
        计算dp[3]只需要计算出比较这k种组合的最大值即可。
     */
    public int maxSumAfterPartitioning(int[] arr, int k) {
        int n = arr.length;
        // dp[i]表示最后一段以arr[i]结尾所能达到的最大值。
        // dp[i]=max(dp[i],dp[i-j]+max(arr[i-j:i])*j)
        int[] dp = new int[n];
        for(int i = 0; i < n; i++){
            int max = 0;
            for(int j = i-1; j >= -1 && i-j <= k; j--){
                max = Math.max(max, arr[j+1]);
                dp[i] = Math.max(dp[i], (j >= 0 ? dp[j] : 0) + (i-j) * max);
            }
        }
        return dp[n-1];
    }
}

410. 分割数组的最大值

难度困难785

给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。

设计一个算法使得这 m 个子数组各自和的最大值最小。

示例 1:

输入:nums = [7,2,5,10,8], m = 2
输出:18
解释:
一共有四种方法将 nums 分割为 2 个子数组。 
其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

示例 2:

输入:nums = [1,2,3,4,5], m = 2
输出:9

示例 3:

输入:nums = [1,4,4], m = 3
输出:4

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 106
  • 1 <= m <= min(50, nums.length)

题解:https://leetcode.cn/problems/split-array-largest-sum/solution/er-fen-cha-zhao-by-liweiwei1419-4/

写在前面的话:

动态规划的写法其实是穷举:按照长度、前缀,枚举最后一个划分,记录每一步结果。细节比较多,需要作图 + 仔细讨论边界情况,并且熟悉二维状态数组、三层 for 循环的写法;

本题的二分查找的思路来源于二分查找的基本思想(应用):查找一个有范围的整数,关键在于利用单调性逼近这个整数。「力扣」上很多问题都基于本题设计,属于「使用二分查找最大值最小化」的一类问题的例题。

class Solution {
    public int splitArray(int[] nums, int k) {
        int sum = 0, max = 0;
        for(int num : nums){
            max = Math.max(max, num);
            sum += num;
        }
        int left = max, right = sum;
        while(left < right){
            int mid = (left + right) >> 1;
            if(!check(nums, k, mid)) left = mid + 1;
            else right = mid;
        }
        return right;
    }
    // 计算 满足不超过「子数组各自的和的最大值」(res) 的分割数
    // 看看分割数能否 <= k
    public boolean check(int[] nums, int k, int maxIntervalSum){
        int splits = 1; // 分割数至少为1
        int curIntervalSum = 0;//当前连续区间的和
        for(int num : nums){
            // 尝试加上当前遍历的这个数,如果加上去超过了「子数组各自的和的最大值」
            // 就不加这个数,另起炉灶
            if(curIntervalSum + num > maxIntervalSum){
                curIntervalSum = 0;
                splits++;
            }
            curIntervalSum += num;
        }
        return splits <= k;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值