leetcode 410. 分割数组的最大值——(每日一难day30)

该博客探讨了一个计算机科学问题,即如何在给定数组和子数组数量m的情况下,分割数组使得各子数组和的最大值最小。通过动态规划的方法,博主详细解释了如何建立状态转移方程并进行优化,以找到最佳的分割方案。示例和代码展示了解决方案的具体实现,适用于算法和数据结构的学习者。
摘要由CSDN通过智能技术生成

410. 分割数组的最大值

给定一个非负整数数组 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] <= 1e6
1 <= m <= min(50,nums.length)

解析:

  • 将问题转化:在前边所有数字的基础上添加一个元素对结果产生的影响。例如:枚举到 7 2 5 8 10中的8时,转化为在7 2 5的基础上添加一个8会结果产生影响。
  • 以8为结尾的子数组可能为8 ,8 5,8 5 2,8 5 2 7,如果m=3,可以怎么分配,这时就枚举8结尾的四个子数组作为第1组,让剩余数字产生剩下的2组。
  • 8自身结合作为一组,那么就需要7 2 5产生两组。
  • 8 5 作为一组,需要7 2 产生两组
  • 8 5 2作为一组 ,7不可能产生两组所以枚举结束。
  • 接下来加入10这个元素,m=3
    以10结尾产生子数组为:10 ,10 8 ,10 8 5,10 8 5 2,10 8 5 2 7
    -10作为一组,则7 2 5 8需要组成2组
    -10 8作为一组,则7 2 5需要组成2组(子问题添加8的时候已经出现)
    -10 8 5作为一组,则7 2组成2组(添加8的时候也出现了)
    -10 8 5 2 作为一组,则7组成2组(不可能枚举结束)

我们用f[i][j]表示,以第 i 个数字为结尾时产生 j 组时,这 j 个子数组各自和的最大值最小为f[i][j].

code

class Solution {
public:
    // 每个元素作为第m个子序列的最后一个元素的最大值最小值
    int splitArray(vector<int>& nums, int m) {
        int n=nums.size();
        // f[0][i],f[i][0]不使用,防止越界,
        //直接用f[i][j]表示以第i个元素结尾,分为j组时,各组最大的值最小的值
        int f[n+1][m+1];
        memset(f,0,sizeof(f));
        // 只有一组情况
        for(int i=1;i<n+1;i++){
            f[i][1]=nums[i-1]+f[i-1][1];
        }
        // 枚举每个元素
        for(int i=1;i<=n;i++){
        	// 枚举以当前元素结尾产生的组数
            for(int j=2;j<=m;j++){
            	// 以当前i元素结尾,产生j组时各组最大值最小
                int tmp=0,tmp2=INT_MAX;
                // 元素i结尾最多可以结合i个元素
                    for(int k=i;k>0;k-- ){
                    tmp+=nums[k-1];
                    // 如果需要的组数>剩余数字数
                    if(j-1>k-1) break;
                    // 前k-1个元素,产生j-1组的最大元素,
                    //与最后一组的最大值
                    int a=max(tmp,f[k-1][j-1]);
                    // 枚举所有与第i个元素结合情况,取最小值
                    tmp2=min(tmp2,a);
                }
                f[i][j]=tmp2;
            }
        }
        return f[n][m];
    }
};
题目描述:给定一个非负整数数组nums和一个整数m,你需要将这个数组成m个非空的连续子数组。设计一个算法使得这m个子数组中的最大和最小。 解题思路: 这是一个典型的二搜索题目,可以使用二查找来解决。 1. 首先确定二的左右边界。左边界为数组最大的值,右边界为数组中所有元素之和。 2. 在二搜索的过程中,计算出分割数组的组数count,需要使用当前的中间值来进行判断。若当前的中间值不够分割成m个子数组,则说明mid值偏小,将左边界更新为mid+1;否则,说明mid值偏大,将右边界更新为mid。 3. 当左边界小于等于右边界时,循环终止,此时的左边界即为所求的结果。 具体步骤: 1. 遍历数组,找到数组中的最大值,并计算数组的总和。 2. 利用二查找搜索左右边界,从左边界到右边界中间的值为mid。 3. 判断当前的mid值是否满足题目要求,若满足则更新右边界为mid-1; 4. 否则,更新左边界为mid+1。 5. 当左边界大于右边界时,循环终止,返回左边界即为所求的结果。 代码实现: ```python class Solution: def splitArray(self, nums: List[int], m: int) -> int: left = max(nums) right = sum(nums) while left <= right: mid = (left + right) // 2 count = 1 total = 0 for num in nums: total += num if total > mid: total = num count += 1 if count > m: left = mid + 1 else: right = mid - 1 return left ``` 时间复杂度析:二搜索的时间复杂度为O(logN),其中N为数组的总和,而遍历数组的时间复杂度为O(N),因此总的时间复杂度为O(NlogN)。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值