建國中學 游承曦
數學上
程式上
計算特定數值下的正確答案
或證明
設計出一套解決問題的流程
能算出該問題在各種情況下的解
Accept (AC) | 答案正確 | 爽。 |
---|---|---|
Wrong Answer (WA) | 答案錯誤 | 找蟲。 (debug) |
Time Limit Error (TLE) | 執行超出時間限制 | 找蟲 或 考慮更好的做法 |
Memory Limit Error (MLE) | 執行超出記憶體限制 | 找蟲 或 考慮更好的做法 |
Runtime Error (RE) | 執行時發生錯誤 | 很多原因 |
Compilation Error (CE) | 編譯錯誤 | 痾... |
語法還不太熟的要趕快練習RRR ><
手上有十張牌上面寫著1~10
將這十張牌由小到大,從左到右排好
正確性(X)
效率(X)
判斷一個演算法的效率
可以發現數字\(n\)在很大的時候,
實際上影響力最大是最高次項,
係數和其他較低次項的並不會有什麼影響,
我們會用「 \(\mathcal{O}\) 」來表示演算法中用到的最高次項
時間複雜度 | 適用範圍 (1秒) |
---|---|
\(O(n!)\)
\(O(2^n)\)
\(O(n^4)\)
\(O(n^3)\)
\(O(n^2)\)
\(O(n \log n)\)
\(n \leq 10\)
\(n \leq 20\)
\(n \leq 50\)
\(n \leq 200\)
\(n \leq 3000\)
\(n \leq 10^6\)
以下五種範例都是由小排序到大
// sort arr[0, n)
for(int i=n-1 ; i>0 ; i--){
for(int j=0 ; j<i ; j++){
if(arr[j] > arr[j+1]){
// swap
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
時間複雜度 \(\mathcal{O}(n^2)\)
額外空間複雜度 \(\mathcal{O}(1)\)
透過不斷地交換
// sort arr[0, n)
for(int i=n-1 ; i>0 ; i--){
int mxp = 0;
for(int j=1 ; j<=i ; j++){
if(arr[j] > arr[mxp]) mxp = j;
}
// swap
int tmp = arr[mxp];
arr[mxp] = arr[i];
arr[i] = tmp;
}
時間複雜度 \(\mathcal{O}(n^2)\)
額外空間複雜度 \(\mathcal{O}(1)\)
每次選擇最小或最大,移動到頭或尾
// sort arr[0, n)
for(int i=1 ; i<n ; i++){
int j = 0;
while(arr[j] < arr[i] && j < i) j++;
int tmp = arr[i];
for(int k=i ; k>j ; k--) arr[k] = arr[k-1];
arr[j] = tmp;
}
時間複雜度 \(\mathcal{O}(n^2)\)
額外空間複雜度 \(\mathcal{O}(1)\)
每次將後方的元素插入到正確位置
時間複雜度 \(\mathcal{O}(n \log n)\)
額外空間複雜度 \(\mathcal{O}(n)\)
採用分治法 (Divide & Conquer)
int arr[N], buf[N];
void MergeSort(int l, int r){ // [l, r]
if(l >= r) return;
int m = (l + r) / 2;
MergeSort(l, m), MergeSort(m+1, r);
int i = l, j = m+1, k = l;
while(i <= m && j <= r){
if(arr[i] < arr[j]) buf[k++] = arr[i++];
else buf[k++] = arr[j++];
}
while(i <= m) buf[k++] = arr[i++];
while(j <= r) buf[k++] = arr[j++];
for(int p=l ; p<=r ; p++) arr[p] = buf[p];
}
int main(){
MergeSort(0, n-1);
}
時間複雜度 最佳 \(\mathcal{O}(n \log n)\) 最差 \(\mathcal{O}(n^2)\)
額外空間複雜度 \(\mathcal{O}(1)\)
一樣是分治法,這裡的例子取pivot為arr[r]
int arr[N];
void QuickSort(int l, int r){ // [l, r]
if(l >= r) return;
int pivot = arr[r], pos = l;
for(int i=l ; i<r ; i++)
if(arr[i] < pivot){
int tmp = arr[i];
arr[i] = arr[pos];
arr[pos++] = tmp;
}
int tmp = arr[pos];
arr[pos] = pivot;
arr[r] = tmp;
QuickSort(l, pos-1);
QuickSort(pos+1, r);
}
int main(){
QuickSort(0, n-1);
}
C++ 都幫你想好惹
#include <algorithm>
using namespace std;
sort( begin, end ); // [begin, end)
#include <iostream>
#include <algorithm>
using namespace std;
int arr[1010];
int main(){
sort(arr, arr + 100); // [0, 99]
sort(arr, arr + 101); // [0, 100]
sort(arr + 3, arr + 87); // [3, 86]
}
期望複雜度 \(\mathcal{O}(n \log n)\)
剛剛的排序都是由小到大,怎麼可能那麼單純
bool cmp();
sort(begin, end, cmp);
#include <iostream>
#include <algorithm>
using namespace std;
bool cmp(int a, int b){
// 前面 後面
return a > b;
}
int arr[1010];
int main(){
sort(arr, arr+1010, cmp);
}
簡稱毒瘤
#include <iostream>
#include <algorithm>
using namespace std;
int arr[1010];
int main(){
sort(arr, arr+1010, [](int a, int b){return a > b;});
}
在cmp的位置改成[](){}
假設我從\(1\)~\(1000\)選一個數字,
你每次猜一個數字,而我回答比答案大or小
直到猜中答案
怎麼樣才是最好的猜法? 猜最少次?
假如亂猜,從1猜到1000,要猜1000次
但如果每次從中間猜
1~1000: 500, 大, 500~1000: 750, ...
最多只需要猜 \(\log_2 1000 \approx 10\) 次
在由小到大的數列上做搜尋,
具有單調性 (monotone),便是二分搜的核心
(  ̄▽ ̄)σ
給一個序列 \(a\),
\(K\) 次詢問數字 \(x\) 存不存在於這個序列?
\(\mathcal{O}(K \log N)\)
[本題為互動題]
電腦隨機生成一個數 \(K\) 介於 \(1\)~\(N\),
每次可以詢問一個數 \(Q\),電腦會告訴你
「\(Q\) 小於 \(K\)」或「\(Q\) 不小於 \(K\)」,
一開始先給定 \(N\),最後回答 \(K\) 的數值
\(\mathcal{O}(\log N)\)
(挑戰) 給你一堆按鈕編號 \(1\) ~ \(2^{31}-1\),每個按鈕旁一開始都顯示數字 \(0\),當按下第 \(K\) 個按鈕可以讓編號 \(K\) 後(包含)的數字 \(1\) 變 \(0\),\(0\) 變 \(1\),經過 \(N\) 次操作後有 \(Q\) 筆詢問,問編號 \(P\) 的按鈕的數字是多少?
\(N \leq 5\times 10^5,\) \(K < 2^{31},\) \(Q \leq 2\times 10^5\)
std::lower_bound()、std::upper_bound()
\(\mathcal{O}(N\log N+Q\log C)\)
對於一些可以知道答案範圍的題目,
又有絕對的單調性,
那不就可以在答案範圍內做二分搜?
很抽象吧直接看題目辣
給 \(N\) 個電信公司需要服務的據點的座標 \(x_i\),並且最多可以架設 \(K\) 個基地台在任一座標位置,每個基地台服務的半徑範圍皆一樣,求半徑至少為多少可以覆蓋所有據點?
\( 1 \leq K < N \leq 50000, \ 0 \leq x_i \leq 10^9\)
我們已經知道至少和至多需要多長的半徑,
那就二分搜可能的答案並驗證它
肝它題序至少有3000字吧...
(超級挑戰)
給你 \(N\) 個數字
求出任兩數字間的第 \(K\) 大差
\(N \leq 32768, \ K \leq \frac{N(N-1)}{2}\)
484跟上面那題很像呢w
(超級超級挑戰)
給你 \(N\) 個數字,求第 \(K\) 大的連續和
連續和定義為任選 \([L, R]\) 的區間總和
\(N \leq 20000, \ K \leq \frac{N(N-1)}{2}\)
這題要用到分治(Divide & Conquer)的做法,
有機會之後再說(x)