Heap/BFS/Basic Graph
2022/03/17
Lecture By luckyanthonyan
Credit by qazwsxedcrfvtg14,zolution
Before We Start
• 最後一小時是練習時間&討論時間
• 有些內容競賽上比較不會用到
Heap
課程影片
• 看了嗎?
• Q&A
Why Heap
• 我們為甚麼需要heap?
- 我們想要知道一堆數字當中的最大(最小)值 - 數字會變多變少
• 我們想要能合併兩個heap
- 為了能合併兩堆數字
Heap 小知識
世界上的Heap有很多種
• 影片中介紹的Binary heap
• Binomial heap
• Pairing heap
• Thin heap
• Fibonacci heap
• ……
Heap 小知識
https://en.wikipedia.org/wiki/Heap_(data_structure)#Comparison_of_theoretic_bounds_for_variant s
Heap 小知識
• 為什麼會有這麼多種Heap?
• 有的常數比較小
• 有的可以O(1)插入
• 有的可以O(1)刪除
• 有的可以O(logN)合併??
• 有的可以O(1)合併??!!
• 因為礙於篇幅,有興趣請自行上網查詢資料:P
Heap 基本操作
priority_queue
• std::priority_queue
• push:將一個元素放入priority_queue中
• top:詢問現在priority_queue中權重最大的元素
• pop:將priority_queue中權重最大的元素拿掉
時間複雜度
• push:O(logN)
• top:O(1)
• pop:O(logN)
Heap push
heap
• heap其實就是一棵complete binary tree
• 因為是complete binary tree所以我們能穩定的控制層數
6 5
2 1
3
heap
• heap性質:父節點的權重不小於子節點的權重
6 5
2 1
3
heap
6 5
2 1
3 push 4
heap
• 將元素放進tree:O(1) 6
5
2 1
3
4
push 4
heap
• 將元素放進tree:O(1)
• 和父節點比較:O(1) 6
5
2 1
3
4
push 4
heap
• 將元素放進tree:O(1)
• 和父節點比較:O(1)*log(n)
• 一直往上浮,直到權重不大於父節點,最多比較log(n)次 6
5
2 1
4
3
push 4
heap
• 將元素放進tree:O(1)
• 和父節點比較:O(1)*log(n)
• 一直往上浮,直到權重不大於父節點,最多比較log(n)次
• 整體來看:O(log(n)) 6
5
2 1
4
3
push 4
heap
• root的權重一定最大,O(1) 6
5
2 1
4
3
top
heap
6 5
2 1
4
3
pop
heap
• root的權重最大,pop掉 5
2 1
4
3
pop
heap
• root的權重最大,pop掉
• 比較兩個子節點,權重大的往上浮:O(1) 5
2 1
4
3
pop
heap
• root的權重最大,pop掉
• 比較兩個子節點,權重大的往上浮:O(1)*log(n)
• 繼續比,最多比log(n)次 5
2 1
4
3
pop
heap
• root的權重最大,pop掉
• 比較兩個子節點,權重大的往上浮:O(1)*log(n)
• 繼續比,最多比log(n)次
• 最後空位用最後一個元素補 5
2
1
4
3
pop
heap
• root的權重最大,pop掉
• 比較兩個子節點,權重大的往上浮:O(1)*log(n)
• 繼續比,最多比log(n)次
• 最後空位用最後一個元素補
• 補上後可能權重又比父親大,還要往上浮一次 5
2
3 1
4 pop
heap
• root的權重最大,pop掉 5
2 1
4
3
pop
heap
• root的權重最大,pop掉
• 將最後一個元素放到root 3
5
2 1
4 pop
heap
• root的權重最大,pop掉
• 將最後一個元素放到root
• 和兩個子節點中權重較大的節點比較:O(1) 3
5
2 1
4 pop
heap
• root的權重最大,pop掉
• 將最後一個元素放到root
• 和兩個子節點中權重較大的節點比較:O(1)*log(n)
• 一直下沉直到權重不小於兩個子節點,最多比較log(n)次 5
3
2 1
4 pop
heap
• root的權重最大,pop掉
• 將最後一個元素放到root
• 和兩個子節點中權重較大的節點比較:O(1)*log(n)
• 一直下沉直到權重不小於兩個子節點,最多比較log(n)次
• 整體來看:O(log(n)) 5
3
2 1
4 pop
Heap Magic
魔法
• 幾個在C++中關於Heap的魔法
• STL
• std::priority_queue
• 黑魔法
• __gnu_pbds::priority_queue
std::priority_queue
• #include <queue>
• std::priority_queue<int> pque;
• priority_queue<int, vector<int>, greater<int> > lque
__gnu_pbds::priority_queue
• #include <ext/pb_ds/priority_queue.hpp>
• __gnu_pbds::priority_queue<int> pque;
• priority_queue<int, greater<int> > lque
• h1.join(h2); //合併h1,h2兩個heap
• pbds heap可以選擇要用哪種heap(改tag)
• 有一些教學:https://emiliatancoding.blogspot.com/2019/05/pbds-priorityqueue.html
Building Heap
• 如果一開始就已經有所有元素了,要建造出一個Heap
• Insertion of Heap: O(logN)
• 所以Build a heap of N element: O(NlogN)
• 其實這個bound不夠緊,可以壓到O(N)
• https://www.geeksforgeeks.org/time-complexity-of-building-a-heap/
Heap Problemset
Problemset
• 只給網址,有些人不喜歡被暴雷
• https://zerojudge.tw/ShowProblem?problemid=b606
• https://tioj.ck.tp.edu.tw/problems/1911
• https://tioj.ck.tp.edu.tw/problems/2026
Flood fill
課程影片
• Q&A
淹水
• 模擬「淹水」的過程很麻煩,幾個方向就要寫幾個很大串的 if,
怎麼辦?
• if (x+1 < n) go(x+1,y);
• if (y+1 < m) go(x,y+1);
• if (x-1 >=0) go(x-1,y);
• if (y-1 >=0) go(x,y-1);
• 提示:有沒有發現所有的 if 其實都長得很像?
淹水
把x,y的變動量拿出來:
(x+1 , y+0) (x-1 , y+0) (x+0 , y+1) (x+0 , y-1)
int dx[]={+1,-1,+0,+0};
int dy[]={+0,+0,+1,-1};
淹水
把x,y的變動量拿出來:
(x+1 , y+0) (x-1 , y+0) (x+0 , y+1) (x+0 , y-1)
int dx[]={+1,-1,+0,+0};
int dy[]={+0,+0,+1,-1};
BFS code
for(int i=0;i<4;i++){
int next_x = x + dx[i];
int next_y = y + dy[i];
if(check(next_x,next_y)){
queue.push(next_x , next_y);
} }
DFS 跟 BFS 的差異
• 我想要從迷宮的起點走到終點
• BFS?
• DFS?
• 我想要用最短路從迷宮的起點走到終點
• BFS?
• DFS?
DFS 跟 BFS 的差異
• 我想要從迷宮的起點走到終點
• BFS?
• DFS?
• 我想要用最短路從迷宮的起點走到終點
• BFS?
• DFS?
• DFS沒辦法得到最短路徑
BFS & DFS
• DFS
• Graph traversal
• BFS
• Graph traversal
• Flood-fill
• Unweighted-Graph shortest path
Problemset
給想做額外練習的同學 題目都不難
• https://zerojudge.tw/ShowProblem?problemid=a597
• https://tioj.ck.tp.edu.tw/problems/1085
• https://codeforces.com/problemset/problem/598/D
Graph
課程影片
• Q&A
存圖
• 相鄰串列
• 相鄰矩陣
比較
• 相鄰矩陣
• 空間複雜度:O(V2)
• 查詢兩個點之間是否有邊:O(1)
• 遍歷一個點v周圍的邊:O(V)
• 增加一條邊:O(1)
• 刪除一條邊:O(1)
• 相鄰串列
• 空間複雜度:O(V+E)
• 查詢兩個點之間是否有邊:O(degree(V))
• 遍歷一個點v周圍的邊:O(degree(V))
• 增加一條邊:O(1)
• 刪除一條邊:O(degree(V))
想想看-是否有環?
• 給你一張有向圖,要怎麼知道這張圖上面有沒有環?
• DFS?
• BFS?
有向無環圖
• 現在有一個工廠,裡面有很多台機器,有些機器所生產 的東西可能會依賴於其他機器,但是保證不會循環。
• 問:找出一個機器的執行順序使得過程中不會有機器依賴 於還沒執行過的機器。
• 拓樸排序!
• 有向無環圖(
DAG
)有向無環圖
• A、G 可以先處理,因為入度為 0
有向無環圖
B 入度為 0,接著處理 B
有向無環圖
Problemset
• 一道簡單的練習題
• https://zerojudge.tw/ShowProblem?problemid=a454
隱式圖搜索
隱式圖搜索
• 有時候我們有狀態 但並不知道整張圖的全貌
• 甚至有可能圖很大張
• 用DFS會TLE
• 所以我們用BFS尋找答案
量杯問題
• 給你 𝑛 (1 ≤ 𝑛 ≤ 5) 個量杯,第 𝑖 個量杯的容量式 𝑚𝑖 的水
• 問你倒出剛好 𝑡 的水,最少要幾個步驟
• 一開始所有量杯都是空的
量杯問題
• 設定 BFS 的狀態
• 在queue裡面放 Hash_table
• 把所有量杯的當前狀態壓進一個long long
• 題目沒有明確地給出「邊」,但還是能 BFS
• 把某些不可能的case先特判掉,避免浪費時間
• 目標容量大於所有量杯的容量
• 即 𝑡 > max
1≤𝑖<=𝑛 𝑚𝑖
• 目標容量不是滿水容量最大公因數的倍數
• 即 𝑡/ (gcd
1≤𝑖≤𝑛
𝑚𝑖) ≠ 0
量杯問題
• 想不到case要判甚麼但就是 TLE 了QQ
• 時間剪枝(x
練習一下
練習一下
• 有一張 N 點 M 邊無向無權圖,有兩個人在 S ,他們分別要走到A、B
• 他們一定要走最短路到達自己的目的地
• 他們想要能一起走越多的路越好
• 請問他們最多能一起經過多少點
練習一下
• 答案滿足以下條件:
• 設 𝑑𝑖𝑠() 為兩點之間的最短距離
• 設 𝑋 是圖上的某個點
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐴) = 𝑑𝑖𝑠(𝑆 → 𝐴)
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐵) = 𝑑𝑖𝑠(𝑆 → 𝐵)
• 對所有滿足條件的點 𝑋 ,取最大的 𝑑𝑖𝑠(𝑆 → 𝑋) 即可
練習一下
• 答案滿足以下條件:
• 設 𝑑𝑖𝑠() 為兩點之間的最短距離
• 設 𝑋 是圖上的某個點
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐴) = 𝑑𝑖𝑠(𝑆 → 𝐴)
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐵) = 𝑑𝑖𝑠(𝑆 → 𝐵)
• 需要好多最短路
• 對每個點做一次 𝐵𝐹𝑆:𝑂(𝑁 × (𝑁 + 𝑀))
練習一下
• 答案滿足以下條件:
• 設 𝑑𝑖𝑠() 為兩點之間的最短距離
• 設 𝑋 是圖上的某個點
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐴) = 𝑑𝑖𝑠(𝑆 → 𝐴)
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐵) = 𝑑𝑖𝑠(𝑆 → 𝐵)
• 需要好多最短路
• 對每個點做一次 𝐵𝐹𝑆:𝑂(𝑁 × (𝑁 + 𝑀))
• 對 𝑆、𝐴、𝐵 各做一次 𝐵𝐹𝑆 : 𝑂 𝑁 + 𝑀
練習一下
• 答案滿足以下條件:
• 設 𝑑𝑖𝑠() 為兩點之間的最短距離
• 設 𝑋 是圖上的某個點
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐴) = 𝑑𝑖𝑠(𝑆 → 𝐴)
• 𝑑𝑖𝑠(𝑆 → 𝑋) + 𝑑𝑖𝑠(𝑋 → 𝐵) = 𝑑𝑖𝑠(𝑆 → 𝐵)
• 需要好多最短路
• 對每個點做一次 𝐵𝐹𝑆:𝑂(𝑁 × (𝑁 + 𝑀))
• 對 𝑆、𝐴、𝐵 各做一次 𝐵𝐹𝑆 : 𝑂 𝑁 + 𝑀
• https://csacademy.com/contest/archive/task/long_journey