POJ 3273--Monthly Expens

来源:互联网 发布:mac播放mkv的播放器 编辑:程序博客网 时间:2024/05/19 08:40

本题要求把一规模为N的数组划分成连续的M份,求最小的每份和限制(限制也即是划分中最大的每份和)。

  1. 设Limit[x]为划分成x份的最小限制,有Limit[x] >= Limit[x+1],显然份数越大限制是不可能越大的,然后上式的等号是存在的,eg:数组为{1, 1 ,2}时,Limit[2] = Limit[3]。
  2. 设限制为L,则利用贪心法从前往后计算的份数m:即每超过限制则我们份数加一。
  3. 贪心中的结论:若份数x < m,则限制Limit[x] > L。此结论可以用归纳法证明,设利用贪心法求得的各份的最后一天分别为Day[i],i = 1,2,...,m。易知到Day[2]为止我们划分为1份的限制肯定是大于L的,则我们假设到Day[i]为止限制为L的最小份数为i(其逆否命题就是前面的结论),显然可以推出到Day[i+1]为止限制为L的最小份数为i+1。
  4. 由1,2,3我们可以得到二分的思路,设left < mid < right,以mid为限制贪心计算的份数为m。若M < m,则由3可知限制要增大,即left = mid+1,反之限制可能减小也可能不变,则right = mid。

#include<cstdio>#define maxN 100005int N,M;short money[maxN];short str2num(char* s)      //读优化{    short m = 0;    while(*s)    {        m *= 10;        m += *s-'0';        s++;    }    return m;}int calcLimit(int limit){    int i;    int monthDay = 1;    int tmpSum = 0;    for(i = 0;i < N;i++)        //贪心法计算Limit下的最小划分份数    {        tmpSum += money[i];        if(tmpSum > limit)        {            tmpSum = money[i];            monthDay++;        }    }    return monthDay;}int main(){    int i;    char strIn[7];    int left,mid,right;    while(~scanf("%d%d",&N,&M))    {        left = right = 0;        getchar();        for(i = 0;i < N;i++)        {            gets(strIn);            money[i] = str2num(strIn);            if(left < money[i])                left = money[i];            right += money[i];        }        while(left < right)        {            mid = (left+right)/2;            if(calcLimit(mid) > M)      //份数大于M,说明限制比mid大                left = mid+1;            else                right = mid;            //限制可能等于mid,也可能比mid小        }        printf("%d\n",left);    }    return 0;}


0 0
原创粉丝点击