# 資訊科技培訓 --- ## 這堂課我們來講講演算法 但不碰程式碼 ---- 首先,我們得認識一下什麼是演算法 > 指一個被定義好的、計算機可施行其指示的有限步驟或次序,常用於計算、數據處理和自動推理。 演算法是有效方法,包含一系列定義清晰的指令,並可於有限的時間及空間內清楚的表述出來。 ---- 可以想像成,我們去設計好幾個步驟 來解決特定問題 這個特定問題很廣泛,你想得到的大部分都有 ---- 像是常聽到YT演算法 > 他就是搜集你常看的,再去找到你可能會喜歡的推薦給你 ---- 但這個例子可能不夠明確 讓我們來看看最早演算法 **歐幾里得演算法** ---- 這個東西是用來求最大公因數的 **透過固定的方式,有一套SOP來求出你要的答案** 後來也在許多領域有應用 ---- 那程式競賽的演算法呢? 基本演算法(APCS說的)大概有: * **排序 (sorting)** * **搜尋 (searching)** * **貪心法則 (greedy method)** * **動態規劃 (dynamic programming)** * **圖論(Graph Theory)** ---- 來看看一些原理跟題目吧! --- ## 排序 (sorting) ---- 排序指的就是 我給你一串數字(陣列) 由小排到大or顛倒過來 ---- 但是在介紹排序之前,我們要介紹兩個名詞 * 時間複雜度 * 空間複雜度 ---- 時間複雜度就是算這個程式跑得快不快 通常會以多項式來表示時間 斜率越大就愈慢 空間複雜度則是會用到多少空間 ---- 正常寫的排序法會是O($n^2$) 但許多演算法都是O($nlogn$) log是對數 Ex:$log_28=3$ ---- 簡單判斷時間複雜度的話就是 一層for迴圈就是一次方 那log怎麼做的呢->每次都剖一半 ---- [來看個排序影片了解一下吧](https://youtu.be/kPRA0W1kECg) ---- 以下介紹幾個簡單的排序演算法 泡抹排序法(Bubble Sort) 選擇排序法(Selection Sort) 插入排序法(Insertion Sort) ---- 泡抹排序法(Bubble Sort) ![image](https://hackmd.io/_uploads/Skzpaot06.png) ---- 選擇排序法(Selection Sort) ![image](https://hackmd.io/_uploads/By36ajFAT.png) ---- 插入排序法(Insertion Sort) 順便講搜尋XD ![image](https://hackmd.io/_uploads/ryXRTotAp.png) --- ## 貪心法則 (greedy method) ---- 這個比較難懂 簡單來說就是找到一個策略 一直照著這個策略走 最後會是好的....嗎? ---- 來看看例題 你現在有1元、5元、10元、50元、100元的硬幣,有很多個 現在要找開459元,最少用幾個硬幣? ---- 簡單吧! 4個100,1個50,1個5,4個1 共10個 策略就是由大往下找 ---- 這個演算法就是直覺想法 但有時候會是對的 ---- 為什麼是有時候? 來看看例題: 你現在有1元、5元、8元的硬幣,有很多個 要找開15元,最少用幾個硬幣 ---- 照你的策略,要4個 但是可以3個5元啊,所以就會錯了 ---- 為什麼呢? 如果你去證明這個貪心是對的話 他必須要是硬幣的大小呈倍數關係 ---- 沒有倍數的話沒關係 來看看另一個演算法! --- ## 動態規劃 (dynamic programming) 他是用來記錄最佳答案、最好狀態的演算法 ---- 對他來說 那個硬幣就是一個蛋糕 但對你們來說有點難 ---- 所以我們來看看一個例題: 小明爬樓梯,一次可以跨1,2步,問走到第7階有幾種走法? 這題其實是排列組合問題 ---- 排列組合解: 設1步走了x個,2走了y個 那麼x+2y=7 | x | 1 | 3 | 5 | 7 | | --- | --- | --- | --- | --- | | y | 3 | 2 | 1 | 0 | $C^4_1+C^5_2+C^6_1+C^7_0=4+10+6+1=21$ ---- 但是用動態規劃呢? 解法:記錄狀態及轉移式 考慮每個階梯的小明是從哪個階梯的小明來的 ---- 圖片說明 所以這題其實就是費氏數列拉 ![image](https://hackmd.io/_uploads/BJb5E3K06.png =50%x) ---- 大家有稍微懂這演算法在幹嘛了嗎? 簡單來說就是,找出問題的轉移式,推向最正確的答案 ---- 有空來想想這些題目怎麼解? ``` 剛開始的硬幣題目 小明長高了,可以一次跨1,2,3階樓梯,問走到第n階的方法數? 小明想搞你,可以一次跨1,2,3階樓梯,但有個條件,不能連續走同樣階數,問走到第n階的方法數? 一台電梯最多載n公斤,有m個客人,每個客人有體重$W_i$,問最少幾趟載完? ``` --- ## 圖論(Graph Theory) ---- 他其實是數學的分支 演算法對初學者來說不好懂又難寫 ---- 圖的儲存與遍歷 ---- 儲存: 1. 鄰接矩陣 2. 鄰接串列 3. 邊集合 ---- 1. G[a][b]=e; 2. G[a].push_back(e); 3. vector.push_back({a,b,e}); ---- ### 圖的遍歷 這很重要,因為很多操作都建立在這上面 ---- DFS 透過遞迴(Stack,LIFO)寫法 ![image](https://hackmd.io/_uploads/rJ40DMIzR.png) ---- BFS 通常用Queue,FIFO輔助 ![image](https://hackmd.io/_uploads/By3GdGUfR.png) ---- dfs code ```cpp= int vis[MAX]; void dfs(int v) { vis[v] = true; for (int u : G[v]) { if (!vis[u]) { dfs(u); } } } ``` ---- bfs code ```cpp= int vis[MAX]; queue<int> BFS; BFS.push(1); vis[1] = true; while (!BFS.empty()) { int v = BFS.front() BFS.pop(); for (int u : G[v]) { if (!vis[u]) { BFS.push(u); vis[u] = true; } } } ``` --- 圖論還有一個很重要的應用:最短路徑 簡單來說就是從A點走向B點 找到之間的最短距離 ---- ![image](https://hackmd.io/_uploads/rkltd2FCp.png =200%x) ---- ![image](https://hackmd.io/_uploads/SJolt2FR6.png) | 1 | 2 | 3 | 4 | 5 | 6 | | --- | --- | --- | --- | --- |:---:| | 0 | 1 | 8 | 4 | 13 | 17 | ---- 其實有一個演算法叫dijkstra 那你會問,啊我知道這要幹嘛? ---- 看這張圖 ![](https://i.imgur.com/hpxMtC2.jpg) ---- 讓我們把他的畫質稍微降低一點 ![](https://i.imgur.com/EhSfvkT.png) ``` 可以發現,其實地圖上的每個點,對於電腦來說就是一個一個得像素點,而這些像素就像我們剛剛解的那個簡單圖一樣,有很多點,點跟點之間的距離一樣 當然,這不是google地圖真正的演算法,如果這是的話,那我們都可以進google工作了 ``` ---- 圖論的應用還有許多 像是拓撲排序 (可以想成你要先完成一個點才能到另一個點) ---- ![image](https://hackmd.io/_uploads/r1GoVzIMR.png) ---- dijkstra ```cpp= #include <bits/stdc++.h> #include <bits/extc++.h> using namespace std; //using namespace __gnu_pbds; #define f first #define s second #define pii pair<int,int> #define pb push_back #define int long long #define AC_code ios_base::sync_with_stdio(0);cin.tie(0); vector<pii> G[200010]; int vis[200010]; signed main(){ AC_code int n,m; cin>>n>>m; for(int i=0;i<m;i++){ int q,w,e; cin>>q>>w>>e; G[q].pb({w,e}); //G[w].pb({q,e}); } priority_queue<pii,vector<pii>,greater<pii>> q; int ans[200010]; for(int i=0;i<200010;i++)ans[i]=1000000000000000; ans[1]=0; q.push({ans[1],1}); while(!q.empty()){ auto tmp=q.top();q.pop(); if(vis[tmp.s])continue; vis[tmp.s]=1; for(auto i:G[tmp.s]){ if(ans[i.f]>tmp.f+i.s){ ans[i.f]=tmp.f+i.s; q.push({ans[i.f],i.f}); } } } for(int i=1;i<=n;i++)cout<<ans[i]<<" "; } ``` ---- 其餘最短路徑演算法: 1. bellman ford(SPFA) 2. Floyd 這裡就不細說ㄌ --- ### 拓墣排序 有向無環圖 ---- ![](https://i.imgur.com/Y6G28Oe.png) ---- ![](https://i.imgur.com/CyV3Wxi.png) ---- ![image](https://hackmd.io/_uploads/ByxNQont0a.png) ---- ![image](https://hackmd.io/_uploads/rkUionF06.png) ---- ![image](https://hackmd.io/_uploads/rJdxh3tA6.png) ---- 寫法:紀錄入度與出度 ---- 相信大家有稍微懂演算法在幹嘛了吧! 我們可以用很多演算法來優化眼前問題 ---- 而許多競賽考的就是這個: 如何設計出好的演算法 在時間內解決題目
{"title":"資訊科技培訓5","contributors":"[{\"id\":\"9ae6df2f-496d-498a-a04c-01b7ac1f6b5b\",\"add\":5436,\"del\":265}]","description":"但不碰程式碼"}
    266 views
   owned this note