POJ3273 Monthly Expense 二分查找

本文转自:POJ3273解题报告
题目链接:POJ3273

大致题意:
给出农夫在n天中每天的花费,要求把这n天分作m组,每组的天数必然是连续的,要求分得各组的花费之和应该尽可能地小,最后输出各组花费之和中的最大值

解题思路:
经典的二分穷举
详细的思路我写在程序注释中,这样会更容易懂
看完我的程序还是无法切入题目的同学,建议先用 朴素的穷举 去左这题,虽然很大机会会超时,但是只是为了辅助理解。本题的二分纯粹是一个优化穷举的工具。

//Memory Time   
//612K   297MS   

#include<iostream>  
using namespace std;  

int n; //天数  
int m; //规定的分组数  

/*判断用当前的mid值能把天数n分成几组*/  
/*通过比较group与m的大小,对mid值进行优化*/  

bool judge_group(int mid,int money[])  
{  
    int sum=0;  
    int group=1;    //当前mid值能把n天分成的组数(初始把全部天数作为1组)  

    for(int i=1;i<=n;i++)  //从第一天开始向下遍历每天的花费  
        if(sum+money[i]<=mid)  //当前i天之和<=mid时,把他们归并到一组  
            sum+=money[i];  
        else               //若 前i-1天之和 加上第i天的花费 大于mid  
        {  
            sum=money[i];  //则把前i-1天作为一组,第i天作为下一组的第一天  
            group++;    //此时划分的组数+1  
        }  

    if(group>m)  
        return false;   //若利用mid值划分的组数比规定的组数要多,则说明mid值偏小  
    else  
        return true;    //否则mid值偏大  
}  

int main(void)  
{  
    while(cin>>n>>m)  
    {  
        int* money=new int[n+1];  //每天花费的金额  
        int low=0;  //下界  
        int high=0; //上界  

        for(int i=1;i<=n;i++)  
        {  
            cin>>money[i];  

            high+=money[i];   //把所有天数的总花费作为上界high(相当于把n天都分作1组)  
            if(low<money[i])  
                low=money[i]; //把n天中花费最多的那一天的花费作为下界low(相当于把n天分为n组)  
        }                     //那么要求的值必然在[low,high]范围内  

        int mid=(low+high)/2;  

        while(low<high)  //可能在low==high之前,分组数就已经=m,但是mid并不是最优,因此要继续二分  
        {  
            if(!judge_group(mid,money))  
                low=mid+1;     //mid值偏小,下界前移  
            else  
                high=mid-1;    //mid值偏大,上界后移  

            mid=(low+high)/2;  
        }  

        cout<<mid<<endl;  //二分搜索最后得到的mid值必然是使得分组符合要求的最优值  

        delete money;  
    }  
    return 0;  
}  

在理解了代码的基础上,自己又对照着写了一遍

/*
poj3273
2017年7月30日22:47:52 
AC代码 
*/
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int a[maxn];
int n,m,ans,sum,mx,mid;

int judge(int l,int r){
    int s=0;
    int group=1;
    for(int i=0;i<n;i++){
        if(s+a[i]<=mid) s+=a[i];//这个地方要满足小于等于,第一次写了小鱼WA 
        else{
            s=a[i];
            group++;
        }
    }
    if(group>m) return false;
    else return true;
}

int main(){
    cin>>n>>m;
    sum=0;
    mx=0;
    for(int i=0;i<n;i++){
        cin>>a[i];  
        sum+=a[i];
        mx=max(mx,a[i]);
    } 
    int l=mx,r=sum;
    mid=(l+r)>>1;
    while(l<r){
        if(!judge(l,r)) l=mid+1;
        else r=mid-1;
        mid=(l+r)>>1;
    }
    cout<<mid<<endl;
    //此处应该输出mid 而不是l 或者 r哦 wa在这 
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值