POJ3273 Monthly Expense 二分法

来源:互联网 发布:流体力学分析软件 编辑:程序博客网 时间:2024/06/07 22:19

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.

二分法
题意是连续n天,每天花若干钱,将n天分为连续的m组,每一组内花费求和,求m组中花费最多的的一组的花费数的最小值。

大致思路是,以为分为m组,和最小的情况应当是m=n,也就是分为n组,这时,最小的和应当是n天中花费最大的一个。和最大的情况是m=1,也就是只分为一组,和就是n天花费的总和。
于是,二分的下界就是n天中花费最大的一天,上界就是n天花费的总额。
mid值就是试图求得的最大花费组的最小值。对于每个mid,判断这个mid值作为最大花费,是否在分m组的情况下,所有n天能被分完。如果能,则尝试更小的mid,也就是ri=mid-1。如果不能,则le=mid+1。
判断m组是否够用的方法是,设定一个值cnt用来记录组数,初始化为1,每次从第一个数开始,只要不超过mid,就累加,如果超过了,那么cnt+1,然后累加和变为当前一个数值,作为下一组的初始累加和。最终得到一共需要的组数。(由于m组是n天中连续日分得的,所以可以通过这种方式求得需要的组数
如果求得的组数小于或等于m,说明这个mid是可行的,但不一定是最后的正确答案,尝试更小的mid。

遇到的问题
在can_bi函数中分组时,当本组累加和超过mid时,没有将新增的一个数赋给下一组的初始值,导致答案错误。
二分法主要是确定上下界,和判断变换上下界的条件。

代码如下:

#include <iostream>#include <stdio.h>using namespace std;int a[100009],le,ri,n,m;bool can_bi(int mid){    int cur,cnt;    cur=0;cnt=1;    for(int j=0;j<n;j++){        cur+=a[j];        if(cur>mid){            cur=a[j]; //新增一个数后,当前组总和超过了mid,那么将下一组的初始值设为a[i]            cnt++;        }    }    return (cnt<=m);}int main(){    while(~scanf("%d%d",&n,&m)){        int rst;        ri=le=0;        for(int i=0;i<n;i++){            cin>>a[i];            ri+=a[i];            le=(a[i]>le)?a[i]:le;        }        while(le<=ri){            rst=0;            int mid=(le+ri)/2;            if(can_bi(mid)){                rst=mid;    //如果可分,那么这个mid就是一个可行的值                ri=mid-1;   //缩小mid的范围,尝试更小的mid是否可分            }else                le=mid+1;        }        cout<<rst<<endl;    }    return 0;}
原创粉丝点击