您的位置:首页 > 其它

TOJ 2789.Monthly Expense(二分经典题目)

2016-07-21 18:08 316 查看
题目链接 : http://acm.tju.edu.cn/toj/showp2789.html

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.

………….

这个题对于像我这种的英语渣来说简直是噩梦,看着书上的解释才算是彻底的明白题意。其实很简单,说白了就是求一个复合函数的最小值。什么养的符合函数呢?就是将n天分为m组,每组包含连续的一天或几天,若第i组的花费为k,则求k=max{ki}的最小值。这道题算法很简单,就是纯粹的二分法,写起来也就短短的几十行。但是这道题正真经典的地方在于你很难想到可以用二分,你也想不到该怎么二分。实际上这道题可抽象为查找一个函数的某一个,而且是一个在某种意义上说是单调函数的查找,因此可以用二分(如果是凸函数则要用到三分)。接下来怎么二分就是这道题比较巧妙的地方了,可以以n天中的最大花费作为low,总和作为high,这样这道题就变成了从区间[ low , high ]中找一个值,使这个值满足题目要求即可,最后还需要提一点:为了使找到的值是最小的,用了一点贪心的思想,尽可能的使每一组的k接近当前的K值,这样就能是找到的满足条件的K值是最小的。

提交时超时了一次,起先非常郁闷:想不到这道题怎么会超时,最后发现把
while(~scanf("%d%d",&n,&m))
写成了
while(scanf("%d%d",&n,&m))
,这样就导致出入死在了while里,不超时就有鬼了。。。

#include <stdio.h>
#include <cmath>
#include <iostream>
using namespace std;
int a[100002];
int n,m;
bool find(int slove){
int num=1;
int sum=0;
for(int i=0;i<n;i++)
if(sum+a[i]<=slove) sum+=a[i];
else{
num++;
sum=a[i];
}
return (num<=m);
}
int main(){
while(~scanf("%d%d",&n,&m)){
int low=0,high=0,mid;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
high+=a[i];
low=max(low,a[i]);
}
while(low!=high){
mid = (low+high)>>1;
if(find(mid)) high = mid;
else low=mid+1;
}
printf("%d\n",low);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  toj 二分