TOJ 55. Number && 二分搜 === [TOC] :::warning 本章會用到排序演算法 顧名思義可以把一堆數字從小排到大或從大排到小 本章會用 STL sort 來排序 如果想知道排序實作細節請點[這裡](https://hackmd.io/@arutoria/HyCsjVdYU) ::: ## 二分搜 ### 二分搜基本概念 :::info **二分搜基本概念** 當我們有一個==遞增或遞減==的序列 我們可以藉由猜中間來排除一半的答案 ::: > 例1. 對方在 1~10 裡選一個數字,你必須嘗試猜到它(答案是 7) > 每次你猜一個答案,他都會告訴你要更大還是更小 我們設答案可能所在的 左界(藍色, 1) 和 右界(紅色, 10) 每次我們都猜左界和右界的中間, 所以猜 (1+10)/2 = 5 答案太小,所以我們把左界移動到 6,因為 1~5 都不可能是答案了 這次中間 = (6+10)/2 = 8 8 太大,因此把右界移到 7。 中間 = (6+7)/2 = 6,把左界移到 7 現在左界 == 右界,裡面只有一個數字,因此答案是 7 ![](https://i.imgur.com/cfraW5U.png) ### 二分搜實作 實際上我們要搜尋數字可能在序列中是不存在的,或有可能有很多個,因此我們需要 upper_bound 和 lower_bound 的幫忙。 * lower_bound: 搜尋第一個 >= x 的位置 * upper_bound: 搜尋第一個 > x 的位置 以下會教怎麼實作 ```cpp int arr[9] = {1,2,3,4,5,5,5,8,10}; // 注意 arr 必須排序過 ``` lower_bound 的 code ```cpp= int lower_bound (int x) { int l = 0, r = 8; while (l<r) // 如果左右界指到同一個地方,代表搜到了 { int m = (l+r)/2; if (arr[m] < x) l = m+1; // 如果 arr[m] < x , 代表 arr[m] 不可能是答案, l = m+1 else r = m; // 但當 arr[m] >= x, arr[m] 還有可能是答案,不能砍到 m } return l; } ``` upper_bound 只有第 7 行不同,連同 arr[m]==x 都要排除掉。 ```cpp= int upper_bound (int x) { int l = 0, r = 8; while (l<r) { int m = (l+r)/2; if (arr[m] <= x) l = m+1; else r = m; } } ``` ## TOJ 55. Number [TOJ 55 題目連結](https://toj.tfcis.org/oj/pro/55/) > 給你 N 個數字,接下來有 M 筆詢問 x, 請輸出 x 在序列中出現幾次。 讓我們觀察 lower_bound 和 upper_bound 的性質。 當我們對 5 二分搜,可以發現 ==uppper_bound 的位置 - lower_bound 的位置 = 5 的個數== ![](https://i.imgur.com/8gvdLI5.png) 因此我們可以用這個性質解決 TOJ 55 :::info STL 有提供 upper_bound 和 lower_bound 語法為 upper_bound(開始指標,結束指標,目標); 他會回傳一個指標, 但如果想要得到整數型別的位置可以這樣寫: ```cpp #include <algorithm> using namespace std; int arr = {1,2,3,5,5,5,5,5,8}; int main() { int pos = upper_bound(arr,arr+9,5) - arr; } ``` ***記得要二分搜前要先排序*** ::: ```cpp #include <bits/stdc++.h> using namespace std; const int N = 1e6+10; int arr[N], n, m; int main() { cin >> n; for (int i=0; i<n; i++) cin >> arr[i]; sort(arr,arr+n); cin >> m; for (int i=0,x; i<m; i++) { cin >> x; cout << upper_bound(arr,arr+n,x) - lower_bound(arr,arr+n,x) << '\n'; // 兩個指標相減可以得到位置的差 // 當元素不存在的時候,兩個函式都會回傳 end(在最後一個元素的下一個), 所以相減還是 0 } } ```