POJ 3273--Monthly Expens

20 篇文章 0 订阅

本题要求把一规模为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 100005

int 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值