基礎演算法
我發現我好像沒有自我介紹過
剩下的大家應該不感興趣還是跳過好了
我是教完基礎語法再來教基礎演算法的講師
關於優節
你就可以拿到 not only 修課證明 but also 優秀結業證書了
並且在最後的小社賽達標
其實只要把 codeforces 我每個 problemset 寫到八成
欸不是各位 我還沒發公告就那麼多了是怎樣 都在偷卷喔
啊等等題目點不開正常
我要先問一下你各位 codeforces handle 再幫你們加進來
之後的課程都很硬 請斟酌服用
讓我們開始ㄅ
Enumeration
Pruning(剪枝)
Greedy
枚舉
1. Subset 子集
2. Permutation 排列
3. 8 queens problem 八皇后問題
Enumeration
Enumeration
枚舉
其實就是暴搜
for (int i = 1; i <= n; i++) {
cout << i << ' ';
}
問你
的數字有哪些
舉個最簡單的例子
註:
以後如果左上角是灰色代表程式碼片段
如果是彩色就是完整可直接執行的程式
Enumeration
Subset
子集
顯然他不會只那麼簡單
問你集合
的子集合有哪些
的子集有:
(空集合)
共
個
有兩種方式可以生成子集
遞迴&位元運算
Enumeration
Method 1
遞迴
vector<int> subset;
void search(int k) {
if (k == n) {
// process subset
} else {
search(k + 1);
subset.push_back(k);
search(k + 1);
subset.pop_back();
}
}
search(0)
search(1)
search(1)
search(2)
search(2)
search(2)
search(2)
search(3)
search(3)
search(3)
search(3)
search(3)
search(3)
search(3)
search(3)
Enumeration
Method 2
位元運算
for (int s = 0; s < (1 << n); s++) {
vector<int> subset;
for (int i = 0; i < n; i++)
if (s & (1 << i)) subset.push_back(i);
// process subset
}
Enumeration
Permutation
排列
問你
有幾種排列方式
的排列有:
共
個
也是有兩種方式可以生成所有排列
遞迴&內建函式
Enumeration
Method 1
遞迴
vector<int> permutation;
vector<bool> chosen(n);
void search() {
if (permutation.size() == n) {
// process permutation
} else {
for (int i = 0; i < n; i++) {
if (chosen[i]) continue;
chosen[i] = true;
permutation.push_back(i);
search();
chosen[i] = false;
permutation.pop_back();
}
}
}
複雜度為:
Enumeration
Method 2
內建函式
vector<int> permutation;
for (int i = 0; i < n; i++) {
permutation.push_back(i);
}
do {
// process permutation
} while (next_permutation(permutation.begin(),permutation.end()));
在
<algorithm>
裡面
的
next_permutation
(需要先排序)
若沒有排序則無法遍歷所有的排列
所以使用前可以先
sort
Enumeration
題目
八皇后問題
在
的棋盤裡面放上八個皇后,
且不互相攻擊,有幾種擺法?
(每個黃后的橫行縱行斜線方向都不能出現其他的皇后)
在
的棋盤裡面放上
後來衍生為
個皇后
對於
:
為一合法解
不合法
Enumeration
解法
void search(int y) {
if (y == n) {
count++;
return;
}
for (int x = 0; x < n; x++) {
if (column[x] || diag1[x+y] || diag2[x-y+n-1]) continue;
column[x] = diag1[x+y] = diag2[x-y+n-1] = 1;
search(y+1);
column[x] = diag1[x+y] = diag2[x-y+n-1] = 0;
}
}
枚舉所有排列
數字為索引
Enumeration
習題
剪枝
1. Grid Paths
2. Optimization 優化
Pruning
題目
問你有幾種可能可以從
格子
的左上角走滿整個平面後到達右下角?
全部共
的其中一種解
使用遞迴枚舉
執行時間:
秒
遞迴次數:
億次
Pruning
Optimization 1
第一次優化
發現兩者對稱
觀察以下兩個圖形
第一步選擇往下(或右),將答案
執行時間:
秒
遞迴次數:
億次
Pruning
Optimization 2
第二次優化
發現他沒走滿
觀察以下圖形
提前到達直接終止
執行時間:
秒
遞迴次數:
億次
Pruning
Optimization 3
第三次優化
觀察以下圖形
發現他撞牆走不滿
執行時間:
秒
遞迴次數:
億次
撞牆且可左右轉,直接終止
Pruning
Optimization 4
第四次優化
不能往前但可左右,直接終止
觀察以下圖形
執行時間:
秒
遞迴次數:
萬次
從
秒
秒!
Enumeration
習題
如果吃 TLE 可以把 vector 換成傳統陣列
貪心/貪婪
1. Coin Problem
2. Scheduling
3. Tasks & Deadlines
Greedy
Greedy
每次選擇都往會構成最佳解的方向走
而最終形成全局最優解
但往往很難證明他是對的
若想湊成
需要
共
個
:
Greedy
決策樹
剛剛的選擇可以畫成以下的決策樹
每個節點都代表著一個決策
把所有可能枚舉出來
每個分岔都代表不同選擇
Greedy 就是找有最佳解的子樹一直走下去
Greedy
Proof
可以先觀察:
其實只需要從最大的開始拿就好
可以得到:
面額為
只會出現一次,
因為如果出現兩次,可以使用更大的面額取代
最多出現兩次
(因為三個
可以用
代替, 同理)
的硬幣,
小的硬幣來構成面額為
或更大的金額
對於每一個面額為
我們沒辦法用比
所以我們證明了 Greedy 很難證明
寫題目的時候可以使用感性理解
其實就只是知道他是對的但懶得證明而已
Greedy
Movie Festival
要怎麼 Greedy?
給定
個時段,問不重疊的線段最多能選幾個?
考慮以下四個線段:
最多選兩個
Greedy
Greedy
how
反例
反例
反例
證明
當我們首先選擇一個結束時間晚於最早結束的情況
之後最多只會有相同數量的選擇
因此,
選擇一個結束時間較晚的事件永遠無法產生更好的解答
1. 最短
2. 最早開始
3. 最早結束
3. 最早結束
Greedy
Tasks & Deadlines
給定
個時段和死線,每完成一個能獲得
死線 完成時間的分數,問最多能獲得幾分?
喔對這可以是負數 死線戰士應聲倒地
Greedy
Greedy
how
根據 從小到大排序就好了
交換之後
證明
少了 , 多了 ,總共增加
小的一定在前面
Greedy
Stick Division
若要將長度為 的木棍分成 三段:
會發現在整顆樹越下面的數字加了越多次
可以把題目變成將需要的長度合併
剩下的算法設計&證明就留給各位了
長度
的木棍,要切成
段已知的長度,
每次切割成本為原始木棍的長度,問最小成本?
最佳解可繪製成以下樹狀圖(不是決策樹)
成本為