洛谷P1182 数列分段`Section II`

题目描述

对于给定的一个长度为N的正整数数列A-iA−i,现要将其分成M(M≤N)M(M≤N)段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列4 2 4 5 142451要分成33段

将其如下分段:

[4 2][4 5][1][42][45][1]

第一段和为66,第22段和为99,第33段和为11,和最大值为99。

将其如下分段:

[4][2 4][5 1][4][24][51]

第一段和为44,第22段和为66,第33段和为66,和最大值为66。

并且无论如何分段,最大值不会小于66。

所以可以得到要将数列4 2 4 5 142451要分成33段,每段和的最大值最小为66。

输入输出格式

输入格式:

 

第11行包含两个正整数N,M。

第22行包含NN个空格隔开的非负整数A_iAi​,含义如题目所述。

 

输出格式:

 

一个正整数,即每段和最大值最小为多少。

 

输入输出样例

输入样例#1: 

5 3
4 2 4 5 1

输出样例#1: 

6

说明

对于20\%20%的数据,有N≤10N≤10;

对于40\%40%的数据,有N≤1000N≤1000;

对于100\%100%的数据,有N≤100000,M≤N, A_iN≤100000,M≤N,Ai​之和不超过10^9109。

解法:二分答案

要找到满足每段和的最大值最小的数。

定义C(d):能找到每段和的最大值小于等于d =>等价于 每一段的和都小于等于d

我们可以贪心的来找,每次都尽量找尽量多的并且和小于d的为一段

如果最后找到的段数小于所需要的段数,则说明存在

因为我们可以拆分任意一段使得段数增加

如果可以r=mid 反之l=mid

最后r即为最后的答案。

然而这样以后还是wa了一个点。

最后发现没有考虑到下界最小必须是最大的那个数组元素。

那么我们直接在读入的时候处理上下界即可(上界为所有数的和)

PS.此外,下界设为0也可以,只需要在判断sum大小的时候判断返回false。

#include 
using namespace std;
const int maxn = 2e5 + 10;
int loc[maxn];
int a, b;
bool C(int mid)
{
    //构造出b段 使得每段都小于等于mid
    int sum = 0, crt = 0;
    int i = 0;
    while(crt < a)
    {
        sum += loc[crt++];
        while(crt < a && sum + loc[crt] <= mid)
            sum += loc[crt++];
        i++;
        sum = 0;
    }
    return i <= b;
}
int main()
{
    //freopen("D:\\Chrome下载文件\\testdata.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> a >> b;
    int l = 0, r = 0;
    for(int i = 0; i < a; i++)
    {
        cin >> loc[i];
        l = max(l, loc[i]);
        r += loc[i];
    }
    for(int i = 0; i < 100; i++)
    {
        int mid = (l + r) / 2;
        if(C(mid))
            r = mid;
        else
            l = mid;
    }
    cout << r << endl;
    return 0;
}

 

你可能感兴趣的:(杂题&暴力&水题)