分而治之
也就是將問題拆成小問題去解決!
Master Theorem
int f(int l, int r){
int m = (l+r)/2;
int a = f(l,mid);
int b = f(mid+1,r);
int tmp = 0;
for(int i = l;i <= r;i++){
tmp += arr[i];
}
return a+b+tmp;
}
int f(int l, int r){
int m = (l+r)/2;
int a = f(l,mid);
int b = f(mid+1,r);
int tmp = 0;
for(int i = l;i <= r;i++){
tmp += arr[i];
}
return a+b+tmp;
}
由於這樣的時間複雜度,與我們所知道的
Merge Sort
有幾分相似,我們知道 Merge Sort 的複雜度為 \(O(n \log n)\)
猜測這個程式的複雜度為 \(O(n \log n)\)!
因此,只要我們能證明 \(T(n) \in O(n \log n)\)
就可以證明這個程式的時間複雜度為 \(O(n \log n)\) 了!
證明方式:
得證,因此這個程式的複雜度為 \(O(n \log n)\)
因此,有人統整出了一種判斷方法
被稱為 主定理
一開始有一個 \(n\) 項的陣列
void merge_sort(int l, int r){
int m = (l+r)/2;
merge_sort(l,m);
merge_sort(m+1,r);
int nxt[r-l+1], p1 = l, p2 = mid+1;
for(int i = 0;i < r-l+1;i++){
if(p1==m){
nxt[i] = arr[p2++];
}else if(p2==m){
nxt[i] = arr[p1++];
}else if(arr[p1] < arr[p2]){
nxt[i] = arr[p1++];
}else{
nxt[i] = arr[p2++];
}
}
for(int i = l;i <= r;i++){
arr[i] = nxt[i-l];
}
}
\(T(n) = 2T(\dfrac n 2) + O(n)\)
根據主定理,得到 \(T(n) = O(n \log n)\)
(Inversions)
接下來,我們要定義一個東西叫做 逆序數對
對於一個陣列 \(A\), \(i \le j\) 且 \(a_i > a_j\)
的數對 \((i,j)\) 被稱作逆序數對
例如: \(A=[3,1,2]\),則 \(A\) 的逆序數對為 \((1,3),(2,3)\)
有兩個逆序數對
對於一個陣列 \(A\), \(i \le j\) 且 \(a_i > a_j\)
的數對 \((i,j)\) 被稱作逆序數對
例如: \(A=[3,1,2]\),則 \(A\) 的逆序數對為 \((1,3),(2,3)\)
有兩個逆序數對
而逆序數對的數量,等同於用氣泡排序時交換的次數
逆序數對又可以長成這樣
等價於木板的交點數量 (圖取自 TOI 2021 初選pC)
那要怎麼找逆序數對數量呢?
很簡單! 我們直接做一次氣泡排序不就好了嗎?
int cnt = 0;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
if(arr[j] > arr[j+1]){
cnt++;
swap(arr[j],arr[j+1]);
}
}
}
不過這樣太慢了,氣泡排序的複雜度是 \(O(n \log n)\)
因此,Merge Sort 就可以派上用場了!
void merge_sort(int l, int r){
int m = (l+r)/2;
merge_sort(l,m);
merge_sort(m+1,r);
int nxt[r-l+1], p1 = l, p2 = mid+1, cnt = 0;
for(int i = 0;i < r-l+1;i++){
if(p1==m){
cnt++;
nxt[i] = arr[p2++];
}else if(p2==m){
nxt[i] = arr[p1++];
}else if(arr[p1] < arr[p2]){
nxt[i] = arr[p1++];
}else{
cnt++;
nxt[i] = arr[p2++];
}
}
for(int i = l;i <= r;i++){
arr[i] = nxt[i-l];
}
}
Merge Sort 計算逆序數對
(Closest Pair of Points)
要在平面上找兩個最近的點
假設點的數量為 \(n\)
那最簡單的方式就是去找每兩個點,然後計算他們的距離
複雜度: \(O(n^2)\)
vector<pt> p;
void dc(int l, int r) {
if (r - l <= 3) {
for (int i = l; i < r; ++i) {
for (int j = i + 1; j < r; ++j) {
ans = max(ans,dis(a[i], a[j]));
}
}
sort(a.begin() + l, a.begin() + r, cmp_y());
return;
}
int m = (l + r) >> 1;
int mid = a[m].x;
rec(l, m);
rec(m, r);
merge(a.begin() + l, a.begin() + m, a.begin() + m, a.begin() + r, t.begin(), cmp_y());
copy(t.begin(), t.begin() + r - l, a.begin() + l);
int now;
for (int i = l; i < r; ++i) {
if (abs(a[i].x - mid) < ans) {
for (int j = now - 1; j >= 0 && a[i].y - p[j].y < ans; --j)
ans = mid(ans,dis(a[i], p[j]));
p[now++] = a[i];
}
}
}
void init(){
sort(p.begin(),p.end(),cmp_x());
ans = 1e18;
dc(0,n);
}