题目的大概意思是:在一条道路上有N个石头,现在要移走M个石头,问移走之后剩下的石头中的最小的距离最大是多少。
首先,根据题目,以及修改数据发现,如果移走的石头越多,那么答案就会越大。从这里就证明了答案是具有单调性的。根据这一点,那么答案就可以用二分答案的方法来求出。
首先,每一个答案都需要验证需移走的石头数是多少,那么,到底要怎么移动,才能让移走的数量最少呢?
例如样例2 11 14 17 21,先实验移走2个到所有石头间的距离都大于12,可以发现,每一次移动,都是移走当前距离与前面一个小于12的。如果移走的是前面的那一个,就会有一些缺陷:你不知道后面会有哪些石头与它的距离是小于12的,如果距离已经是小于12了,而且是最后一个,那么这一种移动方案也不成立。根据贪心的思想来解释,既然前面的已经达到了这个距离,我再增大这个距离对我也没有什么益处,反而移走当前这一个,后面原本小于12的距离也可能会变得大于12,这样要移走的数量自然就少了。这样就可以得到一个最优方案。所以,自然是这样移动是最好的。
算出来移动的个数之后,就可以判断,如果移走的个数比m大,就证明答案要达到这个距离是不可能的事,自然答案就会比当前测试的答案小。否则就可能比这一个大。注意,在二分的时候,也要注意几点:必须要让二分区间达到(】,即右边的一定不成立,而左边的一定成立,而不是让最左点和最右点都能成立,这样二分出来的答案自然就容易不正确。(CCF给出的数据表示,就算没有这么严格,同样能对;但是,如果数据挑一些很困难的数据,答案自然就错了)最后二分出来的答案自然就是左端点(右端点不成立)。
以下为程序:
#include<stdio.h> #include<stdlib.h>
int n,m; int a[100000];
bool _check(int len) { int last=0,move_num=0; for(int i=0;i<n;i++) { if(a[i] - last < len) move_num++; else last = a[i]; }
return move_num <= m; }
int main() { freopen("stone.in","r",stdin); freopen("stone.out","w",stdout);
int l; scanf("%d%d%d",&l,&n,&m); for(int i=0;i<n;i++) scanf("%d",&a[i]); a[n]=l;n++;
int lowerlim=0,upperlim=l+1; while(lowerlim+1 < upperlim) { int middle=(upperlim+lowerlim) >> 1;
if( _check(middle) ) lowerlim=middle; else upperlim=middle; }
printf("%d\n",lowerlim); return 0; } |
最后再说一句,因为二分用递归容易栈溢出(虽然只有50次),所以要用迭代。而且因为可以从起点跳到第一个石头或最后一个石头跳到终点,所以这个也需要算上。