面试 - 最大值最小化(字节)

题目描述

把一个包含n个正整数的序列划分成m个连续的子序列。设第i个序列的各数之和为S(i),求所有S(i)的最大值最小是多少?
例如序列1 2 3 2 5 4划分为3个子序列的最优方案为 1 2 3 | 2 5 | 4,其中S(1),S(2),S(3)分别为6,7,4,那么最大值为7;
如果划分为 1 2 | 3 2 | 5 4,则最大值为9,不是最小。

解法:贪心+分治(C++)

我们将算法分为两个部分:

(1)判断命题

设对于某一数 x x x,是否存在 m m m个连续子序列使得所有的 S ( i ) ⩽ x S(i)\leqslant x S(i)x,如果命题成立最小的 x x x即为最终所求答案。

对于该命题的判断,只需要利用贪心算法从左到右搜索即可。

(2)二分猜数

因为我们也不知道最小的 x x x为多少,所以我们利用二分的方式来查找 x x x

最终,算法的时间复杂度就是 O ( N l o g S ) O(NlogS) O(NlogS),其中 S = ∑ i n u m s [ i ] S=\sum_i nums[i] S=inums[i]

#include <bits/stdc++.h>

using namespace std;

int n, m;
vector<int> nums(1000, 0);

// 判断命题是否成立
bool helper(int x)
{
    int s = 0, t = 0;
    for(int i=0;i<n;i++)
    {
        if(nums[i]>x) return false;
        if(s+nums[i]>x)
        {
            s = nums[i];
            t++;
            if(t>m-1) return false; // t=m-1时,说明存在m-1个划分,m个子序列
        }
        else s += nums[i];
    }
    return true;
}

// 二分猜数
int Solution(vector<int>& nums)
{
    int left = 0, right = 0;
    for(int i=0;i<n;i++)
        right += nums[i];
    while(left<right)
    {
        int mid = left+(right-left)/2;
        if(helper(mid)) right = mid;
        else left = mid+1;
    }
    return left;
}

int main()
{
    cin >> n >> m;
    for(int i=0;i<n;i++) cin >> nums[i];
    int ans = Solution(nums);
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值