1. 程式人生 > >二分答案&【NOIP 2015】跳石頭

二分答案&【NOIP 2015】跳石頭

new 是我 closed none display ctype tps git tdi

二分答案估計是我應該早就學會,但拖到現在才去好好看的套路吧!

二分答案是指在答案具有單調性的前提下,利用二分的思想枚舉答案,將求解問題轉化為驗證結果。首先需要估計答案的上下界,然後不斷取區間中點進行驗證(這就要求答案的驗證應當簡單可行),並通過驗證結果不斷更新答案區間,最終得到答案。不難看出,樸素的枚舉驗證時間復雜度是O(n)的,而二分可以做到O(logn)。更令人欣喜的是,二分答案的問題往往有固定的問法,比如:令最大值最小(最小值最大),求滿足條件的最大(小)值等。

一道模板題練個手:https://www.luogu.org/problemnew/show/P2678


這幾乎是最簡單的二分答案模板題,在明白二分答案後,是可以做到一遍AC的。題目的問法也十分模板(求最短跳躍距離的最大值),答案是單調的,拿走的石頭越多,最短跳躍距離越大。我們可以估計最大的最短跳躍距離(1<=ans<=L)。然後取區間中點,進行驗證。若發現某次跳躍比這個最短跳躍距離還小,那就把這塊石頭拿走。總共拿走的石頭數不超過M,就說明區間中點符合要求,就將區間更新為右半區間(因為要求最大的最短跳躍距離);反之,就將區間設為左半區間。

技術分享圖片
 1 #include<cstdio>
 2 #include<cctype>
 3 inline int get_num() { //讀入優化
 4     int num;
 5     char c;
 6     while((c=getchar())==\n||c== ||c==\r);
 7     num=c-0;
 8     while(isdigit(c=getchar())) num=num*10+c-0;
 9     return num;
10 }
11 const int maxn=5e4+5;
12
int L,n,m,d[maxn]; 13 bool check(int x) { //檢驗答案是否符合要求 14 int last=0,cnt=0; 15 for(int i=1;i<=n;++i) { 16 if(d[i]-last<x) ++cnt; //拿走石頭 17 else last=d[i]; //跳出不影響答案的一步 18 } 19 return cnt<=m?true:false; 20 } 21 int main() { 22 L=get_num();n=get_num();m=get_num();
23 for(int i=1;i<=n;++i) d[i]=get_num(); 24 d[++n]=L; //註意,最終要跳到終點 25 int l=0,r=L+1; 26 while(r-l>1) { 27 int mid=l+(r-l)/2; 28 if(check(mid)) l=mid; //調整區間 29 else r=mid; 30 } 31 printf("%d",l); 32 return 0; 33 }
AC代碼

二分答案&【NOIP 2015】跳石頭