目录
月度开销:
要求:
总时间限制: 1000ms
内存限制: 65536kB
描述:
农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。
约翰打算为连续的M (1 ≤ M ≤ N) 个财政周期创建预算案,他把一个财政周期命名为fajo月。每个fajo月包含一天或连续的多天,每天被恰好包含在一个fajo月里。
约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。
输入:
第一行包含两个整数N,M,用单个空格隔开。
接下来N行,每行包含一个1到10000之间的整数,按顺序给出接下来N天里每天的开销。
输出:
一个整数,即最大月度开销的最小值。
样例输入:
7 5 100 400 300 100 500 101 400
样例输出:
500
提示:
若约翰将前两天作为一个月,第三、四两天作为一个月,最后三天每天作为一个月,则最大月度开销为500。其他任何分配方案都会比这个值更大。
问题分析:
我们先来分析一下题意:约翰希望为自己未来N天的开销做一个预算估计,所以他需要将这N天的预算进行M个财政周期的分划,将N天的预算“连续地”分成M个互不相交的集合:,
记作M个fajo月,并使得这M个Fajo月的预算的最大值最小。我们的目标就是求出这个最小预算。
如果你对于上面的题意分析并不是很理解,我们来看一下样例和提示。样例中给出的预算有N=7天,用集合表示的话就是,我们需要把这个集合“连续地”拆成,这样的话,Fajo月中预算最大值为500。假设有比500更小的答案,那么500这个元素始终无法归入任何一个集合中,所以500就是最优的情况。
通过分析样例,我们似乎发现此题很难,连样例都要简单证明一下才能给出解答,那么这个问题应该怎么来解决?当我们的思维似乎无法对问题直接给出一个完备解法时,我们就要依靠计算机的强大计算能力,通过搜索枚举来解决问题。
但是这一想法又再次“碰壁”。题中说N的范围是1~100000,这个数据过于之大,除了O(1)和O(n)的复杂度以外,基本上都是超时的结果了。如果我们对所有情况进行遍历的话,最差情况是需要次才能算完,这是极其恐怖的次数,不可接受。所以我们有必要对搜索进行适当的优化(二分)。
和N最大为100000相比,每日预算最多10000就显得小了许多。我们知道答案一定在MaxCost与SumCost之间。(MaxCost:单日最大预算,SumCost:所有预算总和)所以我们可以不去搜索集合分划的情况,我们可以直接搜索答案,如果答案直接匹配,输出就可以了。但是直接搜索答案也不能盲目搜索,我们需要“反复横跳”,利用类似二分法寻找方程近似解的方法一步步缩小范围,直到我们最终求出了目标解(范围锁定到一个数上)。对于搜索过程中的节点验证一下是否可将原数据分为M组。如果FajoCount>M,说明当前值过小,如果FajoCount<=M,说明当前值可能偏大。
最终代码:
#include<iostream>
using namespace std;
int main(){
int N;//总天数
int M;//总fajo月数
scanf("%d%d",&N,&M);
int *Cost=new int[N];
int MaxCost=0;int SumCost=0;
for(int i=0;i<N;i++){
scanf("%d",&Cost[i]);
MaxCost=max(MaxCost,Cost[i]);
SumCost+=Cost[i];
}
int Left=MaxCost;
int Right=SumCost;
int Middle=Left+(Right-Left)/2;
while(Left!=Right) {
int FajoCount = 1;
int TempSum = 0;
for (int i = 0; i < N; i++) {
TempSum+=Cost[i];
if (TempSum > Middle) {//如果超过了Middle,说明不含该点之前的点组成一个Fajo月
FajoCount++;
TempSum=Cost[i];
}
else if(TempSum==Middle){//如果刚好等于Middle,说明含该点组成一个Fajo月
FajoCount++;
TempSum=0;
}
}
if(FajoCount<=M){
Right=Middle-1;
}
else{
Left =Middle+1;
}
Middle=Left+(Right-Left)/2;
}
printf("%d\n",Middle);
delete []Cost;
return 0;
}
总结:
二分对于搜索的优化是十分显著的,一定要善用二分法。(光是输入就已经O(n)了,所以多余操作很容易超时)希望这篇文章可以为您带来帮助。