Divide & Conquer
Merge Sort
1. 分成子問題
把序列分成左右兩半
2. 解決子問題
3. 合併子問題
如果我們已經知道兩個排序好的陣列,怎麼合併成一個排序好的陣列?
1
1
2
3
5
6
6
8
9
1 | 2 | 3 | 5 | 8 |
---|
1 | 6 | 6 | 9 |
---|
\(A\)
\(B\)
\(C\)
\(時間複雜度:O(n)\)
\(有 O(\log n)層,每層都是O(n)\)
\(所以就能O(n \log n)排序了!\)
1 | 1 | 2 | 3 | 5 | 6 | 6 | 8 | 9 |
---|
1 | 1 | 2 | 3 |
---|
5 | 6 | 6 | 8 | 9 |
---|
1 | 2 |
---|
1 | 3 |
---|
5 | 8 |
---|
6 | 8 | 9 |
---|
6 | 8 |
---|
2 2 1 3 5 8 6 8 9
void merge_sort(int a[], int l, int r) { //[l, r)
if (r - l <= 1) return;
int mid = (l + r) / 2;
merge_sort(a, l, mid);
merge_sort(a, mid, r); //thus, [l, mid) and [mid, r) are sorted
int sorted[r - l];
int li = l, ind = 0;
for (int ri = mid;ri < r;ri++) { //two pointers
while (li < mid && a[li] <= a[ri]) {
sorted[ind] = a[li];
ind++, li++;
}
sorted[ind] = a[ri];
ind++;
}
while (li < mid) { //insert remaining elements
sorted[ind] = a[li];
ind++, li++;
}
for (int i = 0;i < r - l;i++) a[i + l] = sorted[i];
}
\(逆序數對:在一個序列A中,若存在 i < j 且\\a_i > a_j ,我們稱(i, j)為一組逆序數對\)
給定一個序列,問有多少組逆序數對
\(n \leq 10^5, a_i \leq 2^{31} - 1\)
Master Theorem
\(如果一個演算法的時間函數T(n)可以表示成:\)
\((1)若f(n) \in O(n^{\log_ba - \epsilon}),則T(n)\in O(n^{\log_ba})\)
\((2)若f(n) \in \Theta(n^{\log_ba}),則T(n)\in \Theta(n^{\log_ba} \log n)\)
\((3)若f(n) \in \Omega(n^{\log_ba + \epsilon}),則T(n)\in \Theta(f(n))\)
一個可能比較好懂的形式?
\(如果一個演算法的時間函數T(n)可以表示成:\)
\((1)若d < \log_ba ,則T(n)\in O(n^{\log_ba})\)
\((2)若d = \log_ba,則T(n)\in \Theta(n^d \log n)\)
\((3)若d > log_ba,則T(n)\in \Theta(n^d)\)
\(就是d和 \log_ba比大小,一樣大加log,否則取大的\)
回顧剛剛Merge sort:
每次遞迴我們把問題分成兩個子問題
每個子問題的規模縮小為\(\frac{n}{2}\)
合併的時間複雜度是\(O(n)\)
\(T(n) = 2T(\frac{n}{2}) + O(n)\)
\(\Rightarrow a=2, b=2, d=1\)
\(\because d=1=\log_22=\log_ba\)
\(\therefore T(n) \in \Theta(n \log n)\)
Strassen矩陣乘法
每次遞迴我們把兩個相乘的矩陣各自分成四個小矩陣
每個子問題的規模縮小為\(\frac{n}{2}\)
但我們只需要進行7次小矩陣乘法即可合併
額外的合併時間複雜度是矩陣加法的\(O(n^2)\)
\(T(n) = 7T(\frac{n}{2}) + O(n^2)\)
\(\Rightarrow a=7, b=2, d=2\)
\(\because d=2<\log_27=\log_ba\)
\(\therefore T(n) \in O(n ^{\log_27})\)
\(給定平面上n個點的座標(x_i, y_i),\\求出這n個點中最近的兩個點的距離是多少?\)
\(n \leq 50000, x_i, y_i \leq 2^{31} - 1\)
註:這邊的距離指的是歐幾里得距離
1. 分成子問題
把平面分成左右兩半
2. 解決子問題
3. 合併子問題
如果我們已經知道左右兩邊的最近點對距離,怎麼合併出整個平面的最近點對距離?
\(d_1\)
\(d_2\)
答案會是 min(左, 右, 左右之間)
\(d\)
\(d\)
\(d=min(d_1, d_2)\)
可以注意到:
\(d\)
\(d\)
\(d\)
\(d\)
對於一個點,最多只有8個點在\(y \pm d\)的範圍內
\(T(n) = 2T(\frac{n}{2}) + O(n \log n)\)
\(\Rightarrow O(n \log^2 n)\)
再快一點~
\(\Rightarrow O(\log n)\)
遞迴版
int exp(int a, int b, int m){
if(b==0) return 1;
if(b%2){
return a*exp(a,b-1,m)%m;
} else{
int tmp=exp(a,b/2,m);
return tmp*tmp%m;
}
}
迭代版
int exp(int g, int x, int p) {
int r, c = g % p;
for (r = 1; x > 0; x >>= 1) {
if (x & 1) {
r = (r * c) % p;
}
c = (c * c) % p;
}
return r;
}
\(構造一個1到N的排列\\滿足這個序列不存在⻑度為 3 的等差子序列\)
\(N \leq 10^5\)
計算\(a \times b\)
(可以先做到subtask 4)
直線上有 n < 10^5 個點,第 i 個點有位置pi,依速度 vi 做等速運動,令 d(i, j) 為第 i 個點跟第 j 個點在以後無限時間最短的距離,
求