二分查找---月度开销

原创 2017年12月09日 15:32:23
来源:
题目描述:
农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。
约翰打算为连续的M (1 ≤ MN) 个财政周期创建预算案,他把一个财政周期命名为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<N;i++) for(int i = 0;i<N;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<M。其中,其中total==M说明对N的划分满足条件,可以进一步划分。如果total>M说明划分的组数过多,即D设置的过小,需要调整左边界,在D值更大的范围内搜索;如果total<M则说明划分的组数过少,即上限D设置的过大,需要调整右边界,在D值更小的范围内搜索。
if(total>M)
L = D+1;
else
R = D-1;
参考答案:
#include<math.h>
#include<iostream>
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<N;i++)
{
cin>>exp[i];
max_exp = max(max_exp,exp[i]);
sum+=exp[i];
}
int L = max_exp;
int R = sum;
//cout<<L<<" "<<sum<<endl;
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<N;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<<total<<endl;
if(total<=M)
{
last_D = D;
R = D-1;
}
else
L = D+1;
}
cout<<last_D;
}
不做伸手党

建站+SEO(基础+高级)在线网课

简介 本系列课程从SEO如何月入过万到如何认识搜索引擎、搜索引擎工作原理、SEO常用术语解释、影响页面排名因素分析、符合最快排名的关键词选择等系统讲述了网站优化方法,通过本课程学习,让你从零基础到精通!
  • 2017年06月01日 11:54

POJ1.11 06月度开销

POJ1.11二分查找 06
  • leizhehan
  • leizhehan
  • 2016-08-25 16:14:24
  • 759

题目:3:月度开销

农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。约翰打算为连续的M (1 ≤ M ≤ ...
  • abc15766228491
  • abc15766228491
  • 2017-12-17 21:08:33
  • 187

|NOIOJ|二分|06:月度开销

描述 农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。 约翰打算为连续的M (1 ...
  • Darost
  • Darost
  • 2016-05-31 06:36:13
  • 2446

06:月度开销OJ

总时间限制: 1000ms内存限制: 65536kB 描述 农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 10...
  • sinat_34943123
  • sinat_34943123
  • 2016-05-10 22:29:18
  • 990

【OJ二分06】月度开销

月度开销 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65536kB 描述 农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接...
  • xym_CSDN
  • xym_CSDN
  • 2015-11-23 20:05:08
  • 1428

出来一个月了

出来也快一个月了,看看自己到底学到了什么类?仔细想想学是学到了可是没很大的提高,这不是我想看到的,可是对于这我真的是无能为力啊。现实完全不是我想象中的样子,我还是一个人奋斗,有点累啊。想歇歇脚可是没有...
  • fangyuanjie
  • fangyuanjie
  • 2010-08-09 19:42:00
  • 335

没意识到的一个小问题...

今天在一个网友在帖子里发了一个找错的代码段,我改了一尚一直没找到问题,其实就很简单,错误代码如下: #include #include static struct student {int n...
  • wjling2009
  • wjling2009
  • 2009-12-31 00:58:00
  • 486

Open judge 06月度开销

06:月度开销 总时间限制: 1000ms 内存限制: 65536kB 传送门 描述 农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下...
  • sinat_34550050
  • sinat_34550050
  • 2016-05-29 19:58:23
  • 635

月度开销

月度开销 农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。 约翰打算为连续...
  • xhk2000
  • xhk2000
  • 2016-08-28 19:14:17
  • 474
收藏助手
不良信息举报
您举报文章:二分查找---月度开销
举报原因:
原因补充:

(最多只允许输入30个字)