来源:
http://cxsjsxmooc.openjudge.cn/2017t2winterw4/3/
题目描述:
农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来
N
(1 ≤
N
≤ 100,000) 天里每天需要的开销。
约翰打算为连续的
M
(1 ≤
M
≤
N
) 个财政周期创建预算案,他把一个财政周期命名为fajo月。每个fajo月包含一天或连续的多天,每天被恰好包含在一个fajo月里。
约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽
可能少
。
题目分析:
1.二分查找的核心是找到一个
有序的序列
,然后
折半查找
。很显然,题目中给定的N天的开销序列,不能进行排序否则无法把它们进行结组,进行生成一个月度。
2.那么有序的序列在哪里?
思路1:
很显然,这M个月度最小的开销就是单天开销的最大值max(N),但是这个值可能不能使这里序列结成M个月。接下来就是让扩大这个基准,及令max(N)和临近的X相加生成新的值,然后重新进行测试。
这时整个“扩基”过程就演变成了枚举,时间复杂度是o(n),显然会让程序超时:思路1不可行。
思路2:
这是找到一个更好的广义的有序序列,
X0,X1,X2,...,XN
Y1 = X0+X1,Y2=Y1+X2, YN = YN-1+XN, 很显然Y是有序的;但是,我们并不需要这个细粒度的序列,只需去{N}中的最大值和sum(N)分别作为控制的左右端点即可。
while(L<=R)
{
D = (L+R)
核心操作1;序列N经过划分、结组后是否能满足M的规定
核心操作2:根据N的划分情况进行边界控制
}
核心操作1:
这里需要判断这时的序列在最大开销为D时,是否能满足M的限制,即
1.从左向右开始划分序列N;2.判断生成的月度
总数和M的关系
划分方法1: 划分方法2;
sum_temp = exp[0]; sum_temp = 0;
total = 0; total = 1; 最后一项exp[N-1]没法处理
for(int i = 1;i
{ {
sum_temp+=exp[i]; if(sum_temp+exp[i]>D)
if(sum_temp>D) {
{ sum_temp = exp[i];//重新开始计算
total++; total++; //月度
sum_temp = 0; }
i--; else
} sum_temp+=exp[i];
else if(sum_temp==D) }
{
total++;
sum_temp = 0;
}
else (最后的一项需要独立算成一个month)
{
if(i==N-1)
total++;
}
}
两种方法一个很大的区别是total的初始值,方法一从零开始计算,而方法二从一开始,其原因在于两个方法是否对最后一个月度进行统计。以方法二为例,假设最后两天是400、500,当后sum_temp = exp[N-1]就无法进入循环体,这时的sum_temp显然是一个新的月度,但total并没有自增;假设最后两天是100、300,他们也是一个月度,但是计数total也没有统计他们。
综上可知,方法二没有统计最后一个月度,所以total就从1开始计算。
方法1对i==N-1进行了控制,所有从total=0开始统计也是可以的。
核心操作二:
(边界条件设置,一定要仔细,不要想当然)
通过统计后total有三种状态,total==M ,total>M, total
可以进一步划分
。如果total>M说明划分的组数过多,即D设置的过小,需要调整左边界,在D值更大的范围内搜索;如果total
if(total>M)
L = D+1;
else
R = D-1;
参考答案:
#include
#include
using namespace std;
int main()
{
int N,M;
int exp[100010];
int max_exp = -1,sum = 0;
cin>>N;
cin>>M;
for(int i = 0;i
{
cin>>exp[i];
max_exp = max(max_exp,exp[i]);
sum+=exp[i];
}
int L = max_exp;
int R = sum;
//cout<
int D,sum_temp,total,last_D=0;
while(L<=R)
{
D = (R+L)/2;
//D=500;
sum_temp = exp[0];
total = 0;
bool flag = false;
for(int i = 1;i
{
sum_temp+=exp[i];
if(sum_temp>D)
{
total++;
sum_temp = 0;
i--;
}
else if(sum_temp==D)
{
total++;
sum_temp = 0;
}
else
{
if(i==N-1)
total++;
}
}
//cout<
if(total<=M)
{
last_D = D;
R = D-1;
}
else
L = D+1;
}
cout<
}
不做伸手党