LeeShoWdian
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Help
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
<!-- --- type: slide --- --> <style> .reveal .slides { text-align: left; font-size:35px; } </style> # Shortest Path最短路徑 3/6 --- ### 什麼是最短路徑? 通常會有一張圖,要你算從起點$s$走到終點$t$所需的最小時間or花費 ---- 像是4走到5的時間/花費是4 或是2走到3的時間/花費是7 ![image](https://hackmd.io/_uploads/ByHKSLqca.png =500x) ---- 圖也有分有向圖和無向圖 ![image](https://hackmd.io/_uploads/Hyb8UU9qp.png =500x) ---- 或是有負環或是沒負環 ![image](https://hackmd.io/_uploads/BJ0swLcqp.png =500x) 在最短路徑這個演算法上我們要對應不同的圖 使用合適的演算法 --- ## 存圖技巧 - 鄰接矩陣 - 鄰接串列 ---- ## 鄰接矩陣 <div style="position: absolute; right: 0%; top:90%;"> ![](https://hackmd.io/_uploads/H1UbYU99p.png =400x) </div> <div style="position: absolute; left: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/Sk9MiIcqp.png =450x) </div> 5 <-> 6 花費為 9 matrix[5][6] = matrix[6][5] = 9 ---- ## 鄰接矩陣--存圖 - 有向 ```cpp! int dis[N][N]; void add_edge(int u,int v,int w){//起點u、終點v、權重w dis[u][v]=w; } ``` - 無向 ```cpp! int dis[N][N]; void add_edge(int u,int v,int w){//連接u和v、權重whttps://hackmd.io/_uploads/BytUT8996.png dis[u][v]=w; dis[v][u]=w; } ``` ---- ## 鄰接串列 1 -> 3 權重為 5 表示為 1 : {3, 5} <div style="position: absolute; right: 0%; top:90%;"> ![](https://hackmd.io/_uploads/H1UbYU99p.png =400x) </div> <div style="position: absolute; left: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/BytUT8996.png =450x) </div> ---- ## 鄰接串列--存圖 - 有向 ```cpp! vector<pair<int,int>>graph[N];//pair存{終點,權重} void add_edge(int u,int v,int w){//起點u、終點v、權重w graph[u].push_back({v,w}); } ``` - 無向 ```cpp! vector<pair<int,int>>graph[N];//pair存{終點,權重} void add_edge(int u,int v,int w){//連接u和v、權重w graph[u].push_back({v,w}); graph[v].push_back({u,w}); } ``` --- ## 最短路徑分類 - 單源最短路徑(固定起點、不固定終點) - 全點對最短路徑(不固定起點、不固定終點) ---- ## 單源最短路徑 給你一個圖,一個起點,求出這個起點到每個點的最短路徑 ![](https://hackmd.io/_uploads/SJvzxGCqp.png =500x) ---- ## 全點對最短路徑 給你一個圖,多組起點和終點,求出每組起點到終點的最短路徑 ![image](https://hackmd.io/_uploads/SJfkEf0ca.png =600x) ---- ## 演算法們 - 單源最短路徑 - Dijkstra - Bellman-Ford - 全點對最短路徑 - Floyd-warshall --- ## Dijkstra ---- ## 想法 由於是單源最短路徑,所以會先創一個陣列,這裡叫dis 一開始會把dis[起點]設為0,代表起點到自己的距離是0 然後把dis[其他點]設為INF,再重複做下面的步驟 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ ---- ## 什麼是鬆弛 假設 dis[u][v] = x,如果存在一個點e 使得x > dis[u][e] + dis[e][v] 那麼我們可以更新dis[u][v] = dis[u][e] + dis[e][v] 這樣子我們稱透過e進行了一次鬆弛 ---- dis[2][3] = 5 因為 dis[2][3] > dis[2][1] + dis[1][3] (5 > 4) 我們更新 dis[2][3] = 4 ![image](https://hackmd.io/_uploads/SkCsXNRca.png) ---- 學會鬆弛之後我們就可以看看Dijkstra在做什麼了 假設一開始起點是2,要算下圖的最短距離 ![image](https://hackmd.io/_uploads/ByUNwERcT.png =600x) ---- ## 初始化 先把dis初始化,把起點設為0,其他設為INF(很大的數字) <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/ByE7D4Aqp.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | INF | 0 | | 2 | | 0 | 0 | | 3 | | INF | 0 | | 4 | | INF | 0 | | 5 | | INF | 0 | | 6 | | INF | 0 | </div> ---- 1. `選一個離起點最近並且沒有走過的點u` $\to$ `選2` 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/BkXvoV0qa.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | INF | 0 | | 2 | | 0 | 0 | | 3 | | INF | 0 | | 4 | | INF | 0 | | 5 | | INF | 0 | | 6 | | INF | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. `把點u設定為走過` $\to$ `visited[2]=1` 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/BkXvoV0qa.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | INF | 0 | | 2 | | 0 | `1` | | 3 | | INF | 0 | | 4 | | INF | 0 | | 5 | | INF | 0 | | 6 | | INF | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. `透過所有連接點u和v的邊,去鬆弛點v` <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkMcjN05T.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | INF | 0 | | 2 | | 0 | `1` | | 3 | | INF | 0 | | 4 | | ~~INF~~ `1` | 0 | | 5 | | INF | 0 | | 6 | | ~~INF~~ `7` | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/Hy7JaEA96.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | INF | 0 | | 2 | | 0 | `1` | | 3 | | INF | 0 | | 4 | | 1 | 0 | | 5 | | INF | 0 | | 6 | | 7 | 0 | </div> ---- 1. `選一個離起點最近並且沒有走過的點u` $\to$ `選4` 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkQ7aVC96.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | INF | 0 | | 2 | | 0 | `1` | | 3 | | INF | 0 | | 4 | | 1 | 0 | | 5 | | INF | 0 | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. `把點u設定為走過` $\to$ `visited[4]=1` 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkQ7aVC96.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | INF | 0 | | 2 | | 0 | `1` | | 3 | | INF | 0 | | 4 | | 1 | `1` | | 5 | | INF | 0 | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. `透過所有連接點u和v的邊,去鬆弛點v` <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkmIANRc6.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | ~~INF~~ `5` | 0 | | 2 | | 0 | `1` | | 3 | | ~~INF~~ `2` | 0 | | 4 | | 1 | `1` | | 5 | | ~~INF~~ `5`| 0 | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/Hyn9R4Rca.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | 0 | | 4 | | 1 | `1` | | 5 | | 5 | 0 | | 6 | | 7 | 0 | </div> ---- 1. `選一個離起點最近並且沒有走過的點u` $\to$ `選3` 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/HJqU1HAcp.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | 0 | | 4 | | 1 | `1` | | 5 | | 5 | 0 | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. `把點u設定為走過` $\to$ `visited[3]=1` 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/HJqU1HAcp.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 5 | 0 | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. `透過所有連接點u和v的邊,去鬆弛點v` <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/S1pQeB0cp.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | ~~5~~ `4` | 0 | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rJ6kbHC5p.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | 0 | | 6 | | 7 | 0 | </div> ---- 1. `選一個離起點最近並且沒有走過的點u` $\to$ `選5` 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkD4ZSRca.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | 0 | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. `把點u設定為走過` $\to$ `visited[5]=1` 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkD4ZSRca.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. `透過所有連接點u和v的邊,去鬆弛點v` <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/Bkx2-r0cp.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/r1O6GB0qa.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | 0 | </div> ---- 1. `選一個離起點最近並且沒有走過的點u` $\to$ `選1` 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkEIXr0cT.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | 0 | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. `把點u設定為走過` $\to$ `visited[1]=1` 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkEIXr0cT.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | `1` | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. `透過所有連接點u和v的邊,去鬆弛點v` <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rkEIXr0cT.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | `1` | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | 0 | </div> ---- 1. `選一個離起點最近並且沒有走過的點u` $\to$ `選6` 2. 把點$u$設定為走過 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rJqWEBA56.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | `1` | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | 0 | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. `把點u設定為走過` $\to$ `visited[1]=1` 3. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rJqWEBA56.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | `1` | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | `1` | </div> ---- 1. 選一個離起點最近並且沒有走過的點$u$ 2. 把點$u$設定為走過 3. `透過所有連接點u和v的邊,去鬆弛點v` <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/rJqWEBA56.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | `1` | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | `1` | </div> ---- 這樣就走完ㄌ <div style="position: absolute; right: 0%; top:90%;"> ![image](https://hackmd.io/_uploads/HyL_ES0qp.png =500x) </div> <div style="position: absolute; left: 0%; top:90%;"> | | | dis | visited | | --- | --- | --- | --- | | 1 | | 5 | `1` | | 2 | | 0 | `1` | | 3 | | 2 | `1` | | 4 | | 1 | `1` | | 5 | | 4 | `1` | | 6 | | 7 | `1` | </div> ---- 分析一下可能的複雜度,以下假設$V$為點個數,$E$為邊個數 1. 初始化 $\to$<!-- .element: class="fragment" data-fragment-index="1" --> $O(V)$<!-- .element: class="fragment" data-fragment-index="1" --> 2. 選一個離起點最近並且沒有走過的點$u$ $\to$<!-- .element: class="fragment" data-fragment-index="2" --> $O(V \cdot V)$<!-- .element: class="fragment" data-fragment-index="2" --> 3. 把點$u$設定為走過 $\to$<!-- .element: class="fragment" data-fragment-index="3" --> $O(V \cdot 1)$<!-- .element: class="fragment" data-fragment-index="3" --> 4. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ $\to$<!-- .element: class="fragment" data-fragment-index="4" --> $O(E)$<!-- .element: class="fragment" data-fragment-index="4" --> 總複雜度:<!-- .element: class="fragment" data-fragment-index="5" -->$O(V^2+E)$<!-- .element: class="fragment" data-fragment-index="5" --> ---- ### 來看這個[例題](https://cses.fi/problemset/task/1671) 給你一個圖,圖上有$V$個點,$E$條邊,給定起點問你這個起點到每個點的最短距離。 $1 \le V\le10^5$,$1 \le E\le 2\cdot 10^5$ ---- 顯然剛剛的做法砸下去就會過了,那複雜度呢? $O(V^2+E)$<!-- .element: class="fragment" data-fragment-index="1" --> $\to$<!-- .element: class="fragment" data-fragment-index="1" --> $O(10^{10})$<!-- .element: class="fragment" data-fragment-index="1" --> $\to$<!-- .element: class="fragment" data-fragment-index="1" --> TLE<!-- .element: class="fragment" data-fragment-index="1" --> ---- 看看有哪些步驟的複雜度是可以被優化掉的 1. 初始化 $\to$ $O(V)$ 2. 選一個離起點最近並且沒有走過的點$u$ $\to$ $O(V \cdot V)$ 3. 把點$u$設定為走過 $\to$$O(V \cdot 1)$ 4. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ $\to$ $O(E)$ 總複雜度:$O(V^2+E)$ 因為$V^2$太大,我們嘗試把$V^2$優化掉 ---- 目標 : `選一個離起點最近並且沒有走過的點u` 代表我們需要選一個點u,他的dis[u]要最小,而且vis = 0 大家可以想一下要怎麼樣比$O(V)$還要快選到這個點$u$ ---- 我們可用到priority_queue,他可以在$O(\log_2 n)$的時間復雜度內找到最小/大的值 透過這個 $O(V * V)$ -> $O(V\log_2 V)$ ($10^{10}$ -> $1.6 \times 10^6$) 很明顯這樣複雜度就好了 ---- 總複雜度 1. 初始化 $\to$ $O(V)$ 2. 選一個離起點最近並且沒有走過的點$u$ $\to$ $O(V \cdot logV)$ 3. 把點$u$設定為走過 $\to$$O(V \cdot 1)$ 4. 透過所有連接點$u$和$v$的邊,去鬆弛點$v$ $\to$ $O(E)$ 總複雜度:$O(VlogV+E)$ ---- 若是邊權只有0或1,有沒有更好的優化方式? ---- 使用deque取代priority_queue。 可以發現我們只要維持每次取出最小值 所以邊權為0我們就把他push_front() 邊權為1我們就把他push_back() 這樣可以保持deque內的單調性質 選一個離起點最近並且沒有走過的點的複雜度從 $O(logV)$ 再次降低到 $O(1)$ 這個技巧以後應該還會提到 叫做 $0-1BFS$ ---- ## 實作 我們在priority_queue存入一個pair<int, int> 第一維存dis 第二維存點id 因為pair優先照第一維排序,所以取出來的會是dis最小的 ```cpp priority_queue<pii, vector<pii>, greater<pii>> pq; ``` ---- 範例code: ```cpp= vector<pair<int, int>> E[V]; priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> Q; vector<int> dis[N]; Q.emplace(0, 起點); while(Q.size()) { auto [du, u] = Q.top(); Q.pop(); // 取出當前最近的點 if(dis[u].size() >= 1) continue; // 這個等於已經vis過的點 (已經算出距離) dis[u].push_back(du); // 算出距離 for(auto [v, w] : E[u]) Q.emplace(w + du, v); } ``` ---- 0-1BFS ```cpp= vector<pair<int, int>> E[V]; vector<int> dis[N]; deque<pair<int, int>> Q; Q.emplace_front(0, 起點); while(Q.size()) { auto [du, u] = Q.front(); Q.pop_front(); // 取出當前最近的點 if(dis[u].size() >= 1) continue; // 這個等於已經vis過的點 (已經算出距離) dis[u].push_back(du); // 算出距離 for(auto [v, w] : E[u]) { if (w) Q.emplace_back(w + du, v); else Q.emplace_front(w + du, v); } } ``` ---- ## 小限制 如果圖中有負權邊,Dijkstra這個演算法不能用 因為Dijkstra會取dis最小的點,在有負權邊的情況下會爛掉 ---- 起點為1的時候會爛掉 ![屏幕截图 2025-02-25 003217](https://hackmd.io/_uploads/SkxmJQccJl.png) ---- ## 最短路徑樹 當你給定了一個起點,從這個起點走到任意的點的路徑會形成一棵樹(也有可能是多顆) 以下面的例子來說,最短路徑樹有兩顆 ![image](https://hackmd.io/_uploads/Sy7kFnJop.png) --- ## 下課時間 --- ## Bellman-Ford ---- ## 想法 Bellman-Ford是以每一條邊去做鬆弛,能鬆弛就直接去做鬆弛,直到不能鬆弛為止。 ![image](https://hackmd.io/_uploads/SJJ5Tmyjp.png =600x) ---- 不難發現由於一條最短路最多會經過$n-1$條邊,因此只要對每個邊鬆弛$n-1$次就好。 ![image](https://hackmd.io/_uploads/SJJ5Tmyjp.png =600x) ---- 以每一條邊去做鬆弛 ```cpp! for (int i = 0; i < m; i++){ if (dis[edge[i].u] + edge[i].w < dis[edge[i].v]){ dis[edge[i].v] = dis[edge[i].u] + edge[i].w; } } ``` 複雜度:總共$m$條邊,鬆弛$n-1$次 $\to O(nm)$ ---- ## 負環 可以無限減少花費的一個環 ($1 \to 3 \to 2 \to 1$) ![image](https://hackmd.io/_uploads/H1fflNksT.png) ---- 有負環的話 會發現可以無限進行鬆弛操作 ![image](https://hackmd.io/_uploads/HJFglNJip.png) ---- ## 判斷負環 若是第$n$輪鬆弛還有點被鬆弛到 代表這張圖存在負環 ---- ## SPFA 其實是Bellman-Ford的優化版本 ---- ## 想法 可以發現Bellman-Ford有很多鬆弛操作是多餘的 只有上一次被鬆弛的節點,所連接的邊,才有可能引起下一次鬆弛 ---- ## code 我們用一個queue來維護 [可能會引起鬆弛操作的節點] ```cpp= struct edge { int v, w; }; int s, n; // 起點 點數量 vector<edge> E[MXN]; vector<int> dis(MXN, INF), cnt(MXN), inq(MXN); // cnt記錄最短路經過幾條邊 // inq記錄節點是否在queue裡面 queue<int> Q; dis[s] = 0; Q.push(s); inq[s] = 1; while (Q.size()) { int u = Q.front(); Q.pop(); inq[u] = 0; for (auto [v, w] : E[u]) { if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; cnt[v] = cnt[u] + 1; if (cnt[v] >= n) { // 有負環 因為一般最短路只會經過 n - 1 條邊 } if (!inq[v]) Q.push(v), inq[v] = 1; } } } ``` ---- ## 複雜度 在大部分時候SPFA跑的非常快 但其實最差的情況複雜度還是 $O(nm)$ 當你發現Bellman-Ford TLE的時候 這個時候你就可以用SPFA --- ## Floyd-warshall ---- ## 定義 dp[$k$][$i$][$j$] $\to$ 代表從 $i$ 到 $j$ 只經過 $1$ ~ $k$ 的最短距 ---- ## 初始化 dp[$0$][$i$][$i$] $\to$ 0 dp[$0$][$i$][$j$] if : $i$ 到 $j$ 有邊 dp[$0$][$i$][$j$] $=$ 邊權 else : dp[$0$][$i$][$j$] = $\infty$ ---- ## 轉移 dp[$k$][$x$][$y$] = min(dp[$k-1$][$x$][$y$], dp[$k-1$][$x$][$k$] + dp[$k-1$][$k$][$y$]) ---- ## 實現 ```cpp! for (k = 1; k <= n; k++) { for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { dp[k][i][j] = min(dp[k - 1][i][j], dp[k - 1][i][k] + dp[k - 1][k][j]); } } } ``` 空間複雜度:$O(N^3)$ 時間複雜度:$O(N^3)$ ---- ## 空間優化 可以發現轉移的時候有一維是可以滾動掉的 dp[$k$][$i$][$j$] $\to$ dp[$i$][$j$] 所以空間複雜度可以優化到 $O(N^2)$ ---- Floyd-warshall可以在時間複雜度$O(N^3)$、空間複雜度$O(N^2)$以內做完全點對最短路徑問題 基本上要做全點對最短路徑一定砸Floyd-warshall --- 來練習0.0 [題目連結](https://vjudge.net/contest/697920)

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully