csu-acm2017暑假集训2-二分搜索D - Monthly Expense

Farmer John is an astounding accounting wizard and has realized he might run out of money to run the farm. He has already calculated and recorded the exact amount of money (1 ≤ moneyi ≤ 10,000) that he will need to spend each day over the next N (1 ≤ N ≤ 100,000) days.

FJ wants to create a budget for a sequential set of exactly M (1 ≤ M ≤ N) fiscal periods called "fajomonths". Each of these fajomonths contains a set of 1 or more consecutive days. Every day is contained in exactly one fajomonth.

FJ's goal is to arrange the fajomonths so as to minimize the expenses of the fajomonth with the highest spending and thus determine his monthly spending limit.

Input
Line 1: Two space-separated integers:  N and  M 
Lines 2..  N+1: Line  i+1 contains the number of dollars Farmer John spends on the  ith day
Output
Line 1: The smallest possible monthly limit Farmer John can afford to live with.
Sample Input
7 5
100
400
300
100
500
101
400
Sample Output
500
Hint
If Farmer John schedules the months so that the first two days are a month, the third and fourth are a month, and the last three are their own months, he spends at most $500 in any month. Any other method of scheduling gives a larger minimum monthly limit.


一开始用的二分上下界确定错了,直接left定的是0,right定为其全部的和,因为一直被坑了很久,所以证明一下这样的上下界错误,可能无法确定最优解。
首先说明一下,我们将所需要的最少的但一定满足条件的钱数定为最优解result,将满足条件的钱数定为可行解can。
先分析:
将此题可简化为如下问题
我们要将N个数,m1,m2...mN分成M个集合,且要求其中一个集合中所有元素之和中最大的即是result,而其他分拆的方法所取到的这样的最大值称为可行解can。
即分拆成如下形式:
[m1,m2,m3..mi],[mi+1...m[j]]...[mk..mN]
那么这个result即是M个集合中元素和得最大值。由于M<=N,且每一个数都必须加入到集合中,那么我们很容易可以确定下界最小的话,可以是N个数中最大的一个,即只有一个元素的集合就已经达到了最大值。而上界应该是所有N个数的和(事实上可以确定为N个数中取M个数的和的最大的一个),但由于过于麻烦,就直接取N个数的和作为上界,这里简单证明为什么这样的上界是正确的,这是由于取N个数的和,那么必定可以得到1个集合,而这是可行的,因为这个集合仍然可以进行拆分,或是已得到result。
因为上下界均是可行的(或拆分的集合少于MG个,但仍然可以进行拆分),因此只需要不断进行二分,从而逼近result。
而由于上下界可能是不可行的,此时我们进行二分方法所得到的mid可能会大于result,从而错过最优解,最终得到一个可行解。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const double eps=1e-8;
const double PI=acos(-1.0);
using namespace std;
int main()
{
    int N,M,a[100001];
    while(scanf("%d%d",&N,&M) != EOF){
        int sum = 0;
        int l = -1,r;
        for(int i = 0;i < N;i++){
            scanf("%d",&a[i]);
            if(l < a[i])
                l = a[i];
            sum = sum + a[i];
        }
        l = 0;
        r = sum;
        int s,k,j,mid = 0;
        while(l < r) {
            mid = (l + r) / 2;
            k = s = j = 0;
            while(j < N){
                if(k + a[j] > mid)
                    s++,k = a[j];
                else
                    k += a[j];
                j++;
            }
            s++;
            if(s <= M)
                r = mid;
            else
                l = mid + 1;
        }


        printf("%d\n",r);
    }
    return 0;
}

你可能感兴趣的:(算法)