HLJU1043: 最大值最小化

来源:互联网 发布:台湾网络枪店 编辑:程序博客网 时间:2024/04/16 23:16

                                               最大值最小化

                                                   Time Limit:1 Sec  Memory Limit:128 MB    

Description

把一个包含n个正整数的序列划分成m个连续的子序列(每个正整数恰好属于一个序列)。设第i个序列的各数之和为S(i),设S(1),…,S(m)的最大值为MAX,请通过划分使得MAX最小。
例如序列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,不是最小。

Input

输入包含多组测试样例。以文件尾为结束。
第一行包含两个数字n(0<n<=100000),m(0<m<=n).n表示序列的数字个数,m表示划分为m个子序列。
第二行包含n个正整数a1,…ai,…an.(0<ai<=10^9)

Output

输出MAX的最小值。每个输出占一行。

Sample Input

6 31 2 3 2 5 45 24 2 1 8 3

Sample Output

711解题思想:     二分+贪心。sum 记录数组a的数据和,从0到sum开始二分,每次取中间值(此处的位运算很快),如果check(mid)为true,则把右区间变更为mid,可以这样想,既然求最大值最小化,举例如果10满足。那我们就把它缩到9;反之如果check(mid)为false,则把左区间变更为mid + 1;接下来谈谈check函数,它是一个判断是否能把序列划分为每个序列之和不大于key的m个子序列,有点贪心的思想,主要就是画杠,分成m列需要画m-1个杠,如果最后得出的杠数cnt > m -1 则说明不能分成m列。最后特别说明,本题的ai范围给到10^9,直接用整型sum存的话会超时,所以把sum、left、right、mid都开成long long. AC代码:#include <cstdio>#include <iostream>using namespace std;const int N = 100010;int a[N];int n, m;long long sum;bool check(long long key){    int cnt = 0;  //记录划分线个数    long long s = 0;    for (int i = 0; i < n; i++)    {        if (a[i] > key)//假如有其中一个元素大于x,            //则极端地把划分线分别设在在其左边和右边,都不能使这一个只有一个元素的序列之和不大于x,更不用说所有序列之和不大于x             return false;        if (s + a[i] > key)        {            cnt++;            s = 0;        }        s += a[i];    }    if (cnt <= m - 1)        return true;    return false;}void init(){    sum = 0;    for (int i = 0; i < n; i++)    {        scanf("%d", &a[i]);        sum += a[i];    }}void solve(){    long long left = 0, right = sum;    while (left < right)    {        long long mid = (left + right) >> 1;        if (check(mid)) right = mid;        else left = mid + 1;    }    printf("%lld\n", left);}int main(){    //freopen("in.txt", "r", stdin);    while(~scanf("%d%d", &n, &m))    {        init();        solve();    }    return 0;}
0 0