分割数组的最大值+完成所有工作的最短时间(二分答案)

问题描述

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

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

思路

最大值最小,典型的二分,关键在于如何写check函数
假设x是最大值的最小值,首先先不考虑最小值这个后缀,
即x至少要满足可以将数组连续分成m个和不超过x的连续子数组
根据这句话我们有两种判断方法

  • 1.把数组分成全部小于x的连续子数组最少可以分成cnt组(即每一组都尽量恰好不超过x的分),则cnt必须满足m>=cnt
  • 2.把数组分成m份连续数组,一定存在某种分组,使得每份的和都<=x,否则则说明无法分成全部都<=x的m份连续子数组,说明任何一种分法每一份的和都>x,说明当前猜的x小了

代码

class Solution {
public:
    int n;
    bool check(vector<int>& nums, int x,int m){
        int sum=0,cnt=1; //注意cnt初始化为1而不是0,因为最后一次没有加进去
        for(int i=0;i<n;i++){
            if(sum+nums[i]>x){ // cnt是可以分配为<=x的块的最小块数
                sum=nums[i];
                cnt++;
            }
            else sum+=nums[i];
        }
        return m>=cnt;
    }

    int splitArray(vector<int>& nums, int m) {
        n=nums.size();
        int l=0,r=0;
        for(int i=0;i<n;i++){
            r+=nums[i];
            l=max(l,nums[i]);
        }
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(nums,mid,m)){
                r=mid-1;
            }
            else l=mid+1;
        }
        return r+1;
    }
};

扩展

  • 思路2.把数组分成m份数组,一定存在某种分组,使得每份的和都<=x,否则则说明无法分成全部都<=x的m份子数组,说明任何一种分法每一份的和都>x,说明当前猜的x小了
    思路2对应的题目:

完成所有工作的最短时间
给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。
请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。
返回分配方案中尽可能 最小 的 最大工作时间 。

代码

class Solution {
public:
    int n;
    bool check(vector<int>& jobs, vector<int>& works,int start,int x){
        if(start>=n) return true;
        int cur=jobs[start]; //现在要分配的工作
        for(auto& labor:works){
            if(labor+cur<=x){
                labor+=cur;
                if(check(jobs,works,start+1,x)) return true;
                labor-=cur; // 记得回溯要复原
            }
            if(labor==0||labor+cur==x) break;
        }
        return false;
    }
    int minimumTimeRequired(vector<int>& jobs, int k) {
        n=jobs.size();
        int l=0,r=0;
        for(int i=0;i<n;i++){
            r+=jobs[i];
            l=max(l,jobs[i]);
        }
        while(l<=r){
            int mid=(l+r)>>1;
            vector<int> works(k); // k个工人每个人的工作量
            if(check(jobs,works,0,mid)){ //检查从工作0开始分配给k个工人,且每个工人工作总量不超过mid,是否能将0之后的工作分配完
                r=mid-1;
            }
            else l=mid+1;
        }
        return r+1;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值