這題同時也是NOIP2015 D2T1 跳石頭 stone 原題。
- 總時間限制:
- 1000ms
- 內存限制:
- 65536kB
- 描述
-
每年奶牛們都要舉辦各種特殊版本的跳房子比賽,包括在河里從一個岩石跳到另一個岩石。這項激動人心的活動在一條長長的筆直河道中進行,在起點和離起點L遠 (1 ≤ L≤ 1,000,000,000) 的終點處均有一個岩石。在起點和終點之間,有N (0 ≤ N ≤ 50,000) 個岩石,每個岩石與起點的距離分別為Di (0 < Di < L)。
在比賽過程中,奶牛輪流從起點出發,嘗試到達終點,每一步只能從一個岩石跳到另一個岩石。當然,實力不濟的奶牛是沒有辦法完成目標的。
農夫約翰為他的奶牛們感到自豪並且年年都觀看了這項比賽。但隨着時間的推移,看着其他農夫的膽小奶牛們在相距很近的岩石之間緩慢前行,他感到非常厭煩。他計划移走一些岩石,使得從起點到終點的過程中,最短的跳躍距離最長。他可以移走除起點和終點外的至多M (0 ≤ M ≤ N) 個岩石。
請幫助約翰確定移走這些岩石后,最長可能的最短跳躍距離是多少?
- 輸入
-
第一行包含三個整數L, N, M,相鄰兩個整數之間用單個空格隔開。
接下來N行,每行一個整數,表示每個岩石與起點的距離。岩石按與起點距離從近到遠給出,且不會有兩個岩石出現在同一個位置。 - 輸出
- 一個整數,最長可能的最短跳躍距離。
- 樣例輸入
-
25 5 2 2 11 14 17 21
- 樣例輸出
-
4
- 提示
- 在移除位於2和14的兩個岩石之后,最短跳躍距離為4(從17到21或從21到25)。
1 #include <stdio.h> 2 int river[50020]; 3 int main() 4 { 5 int l,r,n,m; 6 int i,j,mid; 7 int ans; 8 scanf("%d%d%d",&ll,&n,&m); 9 for(i=1;i<=n;i++) 10 scanf("%d",&river[i]); 11 river[0]=0; 12 river[n+1]=ll; 13 l=0; 14 r=ll+1; 15 while(l+1<r) 16 { 17 mid=(l+r)/2; 18 ans=0; 19 i=0; 20 while(i<=n) 21 { 22 j=i+1; 23 while(river[j]-river[i]<mid&&j<=n+1) 24 j++; 25 ans+=j-i-1;//因為算的是間距,所以還要-1 26 i=j; 27 } 28 if(ans<=m) l=mid; 29 else r=mid; 30 } 31 printf("%d\n",l); 32 return 0; 33 }
這題二分的思路是這樣的:
先存好全部石頭數據
然后用二分取一個最長的最短跳躍距離,即mid
當選出一對石頭j與i,它們之間的距離>=mid,就把它們中間的石頭算作移除,移除數量就是j-i-1
移除之后,從石頭j開始,繼續往后算,除非算到了n+1,自然就不能繼續算下去了
算完之后,用總移除數ans對比實際可以移除的石頭數m
當ans<m,說明取的mid間距太短,導致某些石頭間的距離大於mid時無法移除石頭,所以l=mid
當ans==m,別急,也許某些石塊移除后,兩塊石頭的距離比mid大,說明mid可能還是小了,所以l=mid
當ans>m,說明取的mid太長,導致本來對某對石頭進行移除操作就合適的時候還繼續往下找,所以r=mid
最后l==r,輸出l
可以舉一個極端的例子說明ans與m的比較
假設石頭是這樣的:
0 20 23 26 29 31 51 (r=52)
只允許移除3塊石頭。這樣的情況下自然是移除中間3塊石頭,不然最短距離將會是3或是6。
設mid為(0+52)/2=26,則會移除20、23兩塊石頭,再移除29、31兩塊石頭,ans>m,r=mid=26
mid=(0+26)/2=13,則20與0空移除一次,再移除23、26、29、31,同理r=mid=13
mid=(0+13)/2=6,則先空移除,然后移除23,移除29,31與51空移除,ans<m,l=mid=6
mid=(6+13)/2=9,移除23、26、31。ans==m了,就行了嗎?不,明顯20與29距離是9,還可以更長為11,那就加長試試,l=9
mid=(9+13)/2=11,移除23、26、29。此時還不能確定11就是最優解,因為還有12,所以l=11
mid=12,移除了23、26、29、31,所以r=12
mid=11,又區間是[l,r)左閉右開型的,即現在能確定距離就是mid=l=11了。
Ps 做了這么多二分,果然中值賦值條件是難點所在啊!