題目:
一年一度的“跳石頭”比賽又要開始了!
這項比賽將在一條筆直的河道中進行,河道中分布着一些巨大岩石。組委會已經選擇好了兩塊岩石作為比賽起點和終點。在起點和終點之間,有N塊岩石(不含起點和終點的岩石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達終點。
為了提高比賽難度,組委會計划移走一些岩石,使得選手們在比賽過程中的最短跳躍距離盡可能長。由於預算限制,組委會至多從起點和終點之間移走M塊岩石(不能移走起點和終點的岩石)。
輸入文件名為 stone.in。
輸入文件第一行包含三個整數L,N,M,分別表示起點到終點的距離,起點和終點之間的岩石數,以及組委會至多移走的岩石數。
接下來N行,每行一個整數,第i行的整數Di(0 < Di < L)表示第i塊岩石與起點的距離。這些岩石按與起點距離從小到大的順序給出,且不會有兩個岩石出現在同一個位置。
輸出文件名為stone.out。
輸出文件只包含一個整數,即最短跳躍距離的最大值。
25 5 2
2
11
14
17
21
4
對於20%的數據,0≤M≤N≤10。 對於50%的數據,0≤M≤N≤100。
對於50%的數據,0≤M≤N≤100。
對於100%的數據,0≤M≤N≤50,000,1≤L≤1,000,000,000。
咋說呢,這題一開始看覺得:哎呀,肯定動規。然后看數據:10^9,然后瞬間懵逼了。於是上網查:要用個東西叫做……二分答案。
先上代碼再解釋:
#include <iostream> using namespace std; int l,n,m,a[50005],ans; bool check(int dis) { int count=0,last=0; for(int i=1;i<=n;i++) if(a[i]-last<dis)count++; else last=a[i]; if(count>m)return 0;return 1; } int main() { ios::sync_with_stdio(0); cin>>l>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; a[n+1]=l; int fl=0,fr=l; while(fl<=fr) { int mid=(fl+fr)/2; if(check(mid))fl=mid+1,ans=mid; else fr=mid-1; } cout<<ans; return 0; }
嗯,啥意思呢?輸入我就不講了,然后這個fl,fr是什么呢?
二分查找肯定會吧?【不會的先洗洗睡吧別往下看了】這個fl,fr就是查找答案的范圍的左邊界和右邊界。查找啥呢?都說了是二分答案,你說二分啥?二分距離唄。意思是我們現在要找到一個最大值的距離,這個距離能保證只搬走m或m以內個石頭就能讓任意兩個相鄰的石頭之間的距離≥這個距離。我們就來二分查找滿足這個條件的最大的距離。
先是從0~L之間查找這個距離,取中間值,也就是L/2,然后看L/2這個距離是否滿足上面那個條件,如果滿足那個條件,就把二分查找的左邊界調到中間值(也就是L/2)的右邊一個去,這樣再次查找的范圍就排除了不可能存在最優解的左半邊(因為左半邊所有滿足或者不滿足上面那個條件的所有距離都會小於L/2這個距離,所以不可能存在(不然為啥不選L/2這個值呢?L/2也滿足那個條件啊)),並且在這里還要記住中間值,因為他是目前的最優解。如果中間值(當前是L/2)不滿足那個條件,那L/2的右邊的所有距離也都不可能存在某個距離能滿足那個條件了(要不然L/2也會滿足那個條件的,對吧?),所以就把查找的右邊界調到中間值的左邊一個去。
當我們處理完上一步后,就得到了一個新的查找區間——原區間的左半邊或者右半邊。再在這個查找區間用上面的方法查找,直到區間里沒有數字了,查找結束。輸出答案。
附上二分答案模板:
int l,r,mid,ans; while(l<=r) { mid=(l+r)/2; if(check(mid))l=mid+1,ans=mid; else r=mid-1; }