題意:牛要到河對岸,在與河岸垂直的一條線上,河中有n塊石頭,給定河岸寬度l,以及每一塊石頭離牛所在河岸的距離,
現在去掉m塊石頭,要求去掉m塊石頭後,剩下的石頭之間以及石頭與河岸的最小距離的最大值。
用二分做,但是開始寫了三個版本的二分,全都wa。
無賴看了別人的二分,還是不理解,為什麼他們寫的就能過。
反覆思索後,終於明白了:關鍵在於題目求的是什麼。
做題思想:二分所求的最小距離的最大值mid,記錄可以去掉的石頭塊數cnt(注意:當相鄰的石頭的距離小於等於mid,就可以去掉),
若cnt > m,h = h - 1 (這裡是比較難理解的,也是我糾結挺久的地方),此時,應當回過頭來看一下題目求的是什麼:
尋找乙個長度mid,使得可以去掉m塊石頭,剩下的石頭中,石頭間的最小距離為mid。
但是cnt記錄的是:相鄰距離小於等於mid的塊數,所以存在這樣的一種情況 ---- cnt記錄的所有石頭中,
有很多塊石頭間的距離是等於mid,而距離小於mid的石頭的塊數是小於等於m的,此時,若將h的值減一,
那麼h的值就變成乙個小於題目所求的答案了。
舉個例子就懂了:假設有9塊石頭,首尾的數都表示河岸。
石頭的編號 1 2 3 4 5 6 7 8 9
石頭到河岸的距離 0 4 5 7 9 12 16 19 23 26 28
相鄰的距離 4 1 2 2 3 4 3 3 3 2
假設l = 1,h = 5, m = 4 則mid = 3, 此時去掉的石頭的編號為:2,3,5,7,9 cnt = 5
按照程式,h = h - 1 = 4,此時mid = 2,去掉的石頭的編號就為:2,4,9 cnt = 3(cnt當然也有可能cnt == m,總之就是cnt > m不成立了)
也就是說,之後求得的cnt <= m恆成立, 即題目的答案不在 l 和 h 之間了(這點
之前是讓我很費解的地方),更準確地說,答案就是執行這次h-1之前的h值。
若cnt <= m,則l = l + 1,討論一下:若cnt < m,明顯當前的mid小了,l = l + 1;若cnt == m,則去掉cnt塊石頭後,
剩下的石頭的最小值必然是大於mid的,所以進行操作l = l + 1。
然後解決上面遇到的問題,因為h已經小於答案了,而答案就是h+1,之後繼續二分cnt <= m恆成立,
l 不斷自增,直到跳出迴圈,因為答案為h+1,我們返回 l 的值,那麼while的判斷條件就是 l <= h,
當 l 自增到 h + 1時就跳出迴圈,l == h + 1,正好就是答案。
寫了這麼多,就是分析了一下二分演算法執行的過程,因為以前用的二分while判斷條件都是 l < h,看樣子以後做二分得多注意了,
稍不注意就會有很多致命的小bug,一定要將對 l 和 h 的操作以及 while的判斷條件結合起來考慮,要對二分演算法進行靈活的變化,
沒有一成不變的模版,具體問題具體分析。
**:
#include #include #include using namespace std;
int l, n, m;
int d[50005];
bool cmp(int a, int b)
int bsearch(int l, int h, int k)
if (cnt > k) h = m - 1;
else l = m + 1;
}return l;
}int main()
POJ 3258 二分 貪心
poj 3258 二分 貪心 一條線段兩個端點之間的距離是l,兩端點之間分布著n個點,這n個點把線段分成了n 1份,現在讓你最多去掉 第一次讀錯題想了很久不知道怎麼做,remove是去掉不是移動,m個點,問n 1份線段最小值的最大值是多少 1 l 109,0 m n 50000 分析類似poj 32...
POJ 3258青蛙過河 二分答案
description 有一條寬度為l 1 l 1,000,000 的河。河中間有n 0 n 20000 塊石頭,青蛙從河西岸經過這n個石塊後,順利跳到了河的東岸。設河中間每個石塊距離西岸的距離為di 其中di大於0小於l 注意 di是距離起始河岸的距離。小明閒著沒事,想移掉河中間的m 0 m n ...
poj 3258 二分最小值最大
題意 有一些石頭排成一條線,第乙個和最後乙個不能去掉。其餘的共可以去掉m塊,要使去掉後石頭間距的最小值最大。解析 二分石頭,最小值最大。include include include include include include include include include include in...