1. 程式人生 > 實用技巧 >二分查詢與二分答案

二分查詢與二分答案

前言

注意:每個人有每個人的寫法。
這個重要且基礎的點,虎哥和老姚都以為對方講了,可誰都沒講。並且老姚的二分答案我看不懂,然後借鑑了oi-wiki好理解的。
由於老姚的mid+1的不確定性,我是搞不明白,請看到的學長解釋一下老姚的。

二分查詢

二分搜尋,也稱折半搜尋、二分查詢,是用來在一個有序陣列中查詢某一元素的演算法。
它每次考察陣列當前部分的中間元素,如果中間元素剛好是要找的,就結束搜尋過程;如果中間元素小於所查詢的值,那麼左側的只會更小,不會有所查詢的元素,只需要到右側去找就好了;如果中間元素大於所查詢的值,同理,右側的只會更大而不會有所查詢的元素,所以只需要到左側去找。
code:

int Search (int l,int r,int x)
{
      int mid;
      while(l<=r)
      {
            mid=l+((r-l)>>1);
            if(a[mid]<x) mid=l-1;
            else if(a[mid]>x) mid=r+1;
            else if(a[mid]==x) return mid;
      }
      return -1;
}

注意,這裡的有序是廣義的有序,如果一個數組中的左側或者右側都滿足某一種條件,而另一側都不滿足這種條件,也可以看作是一種有序(如果把滿足條件看做1,不滿足看做0,然後再加一個Check(mid))。所以,二分搜尋法可以用來查詢滿足某種條件的最大(最小)的值,也就是二分答案。

二分答案

要想使用二分搜尋法來解這種「最大值最小化」的題目(wiki上的)
需要滿足以下三個條件:
1.答案在一個固定區間內;
2.可能查詢一個符合條件的值不是很容易,但是要求能比較容易地判斷某個值是否是符合條件的;
3.可行解對於區間滿足一定的單調性。換言之,如果x是符合條件的,那麼有x+1或者x-1也符合條件。(這樣下來就滿足了上面提到的單調性)

左側合法的最大值

code:

int Search(int l,int r)
{
      ++r;//左閉右開
      int mid;
      while(l+1<r)//不相鄰
      {
            mid=l+((r-l)>>1);
            if(Check(mid)) l=mid;//l一直是合法的
            else r=mid;
      }
      return l;//返回合法
}

為什麼左閉右開

因為搜到最後,會這樣:

合法 合法 合法 合法 非法 非法 非法
最小值 ··· l mid r ··· 最大值

合法 合法 合法 非法 非法 非法 非法
最小值 ··· l mid r ··· 最大值

然後會

合法 合法 合法 非法 非法 非法
最小值 ··· l\mid r ··· 最大值

合法 合法 合法 非法 非法 非法
最小值 ··· l r\mid ··· 最大值

模擬是正確的,但為什麼不是左閉右閉呢?是因為如果答案是最大值,那就wa了(它會返回最大值-1)。

右側合法的最小值

code

int Search(int l,int r)
{
      --l;//左開右閉
      int mid;
      while(l+1<r)//不相鄰
      {
            mid=l+((r-l)>>1);
            if(Check(mid)) r=mid;//r一直是合法的
            else l=mid;
      }
      return r;//返回合法
}

為什麼左開右閉

因為搜到最後,會這樣:

非法 非法 非法 非法 合法 合法 合法
最小值 ··· l mid r ··· 最大值

非法 非法 非法 合法 合法 合法 合法
最小值 ··· l mid r ··· 最大值

然後會

非法 非法 非法 合法 合法 合法
最小值 ··· l\mid r ··· 最大值

非法 非法 非法 合法 合法 合法
最小值 ··· l r\mid ··· 最大值

模擬是正確的,但為什麼不是左閉右閉呢?是因為如果答案是最小值,那就wa了(它會返回最小值+1)。