🐑本文作者:C++橙羊🐑
🎮🔊本文代码适合编译环境:DEV-C++💻
✨🧨温馨提示:此文乃作者心血,如要转载请标注版权,否则视为抄袭!🎉🎠
今天橙羊继续为大家带来每日一练,话不多说,看题:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
注意:
下面的题目中:
橙色字是废话(可以不看)
红色字是数组大小
蓝色字是目的
月度开销
题目描述
Farmer John是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算并记录下了接下来 N (1 ≤ N≤ 100,000) 天里每天需要的开销。Farmer John打算为连续的M(1 ≤ M≤ N) 个财政周期创建预算案,他把一个财政周期命名为一个fajo月。每个fajo月包含一天或连续的多天,每天都被恰好包含在一个fajo月里。
约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。
输入格式
第一行包含两个整数N,M,用单个空格隔开。
接下来N行,每行包含一个1到10000之间的整数,按顺序给出接下来N天里每天的开销。
输出格式
一个整数,即最大月度开销的最小值。
输入输出样列
输入样例1:
7 5 100 400 300 100 500 101 400输出样例1:
500【耗时限制】1000ms 【内存限制】64MB
题目是什么意思呢?我们拿样例举个栗子:
若约翰将前两天作为一个月,第三、四两天作为一个月,最后三天每天作为一个月,则最大月度开销为500。其他任何分配方案都会比这个值更大。
那么,这道题第一眼你就会想到——枚举。
那枚举可以吗?我们来试一试:
题目要求最大月度开销的最小值,从小到大枚举所有可能值,判断是否可行。
枚举需要确定上下界,那么最大月度开销可能的最小值和最大值分别是多少?
最小值:如果可以分解为N个fajo月,可以获得最小值:“消耗最大的那一天的消耗”
最大值:如果只可以分解为1个fajo月,可以获得最大值:“所有天消耗的总和”。
即从小到大枚举最大月度开销的值x,判断是否可行,首个可行的值就是问题的解。
那么这段代码就是:
for(int x=minn;x<=maxn;x++){
if(check(x)){
ans=x;
break;
}
}
那么如何判断答案x是否满足要求?
将每一天安排进一个fajo月,在每个fajo月的花销不超过X的前提下,判断所需的fajo月是否<=M。
时间复杂度:O((maxn-minn)*n)
这不明摆着超时嘛……
那么能不能优化呢?答案是——能!
首先问两个问题:如果答案x不可行(需要的fajo月 > M),那么x − 1有没有可能可行?
不可能,因为答案越小,需要分隔出的fajo月越多, 如果x不可行,那么x−1需要更多的fajo月,更不可行,同理所有
如果答案x可行(需要的fajo月 <= M) ,那么x+1有没有可能可行?
一定可行,因为答案越大,需要分隔出的fajo月越少, 如果x可行,那么x+1需要的fajo月一定不大于x ,所以一定可行。但是题目要求答案越小越好,所以如果x可行,那么所有>x的值都不用考虑了。
所以可以知道:该问题的答案具有单调性。
既然是单调性,那就有了——二分答案下界
所以……解析?
解析+注释:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
bool check(int x){//判断函数
int fajo=1,sum=a[1];
for(int i=2;i<=n;i++){
if(sum+a[i]<=x){
sum+=a[i];
}
else{
fajo++;
sum=a[i];
}
}
return fajo<=m;
}
int main()
{
cin>>n>>m;
int l=0,r=0;
for(int i=1;i<=n;i++){
cin>>a[i];
l=max(l,a[i]);//最大值为每天花费的累加和
r+=a[i];//最小值为花费最多的那天的累加和
}
r++;//二分答案一般为左闭右开
while(l<r){
int m=(l+r)/2;
if(check(m))r=m;//如果满足就尝试更小的
else l=m+1;
}
cout<<l;//求下界,l是首个满足条件的值
return 0;
}
好了,今天就到这了。我是橙羊,拜拜~