# 圖論 負權最短距離 [![hackmd-github-sync-badge](https://hackmd.io/2AgFp0PORdahaCEV6ETkbQ/badge)](https://hackmd.io/2AgFp0PORdahaCEV6ETkbQ) ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` ---- ## 為何不能用dijkstra 使用dijkstra權重需要都 $\ge0$ 權重$\lt 0$會怎樣? ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` 會出現負循環 那要如何解決? <!-- 就要用到的就要用到新的演算法 bellman ford algorithm --> --- ## 介紹 bellman ford ---- ### 特點 1. 求點對點最短距離。 2. 允許權重<0 3. 可以檢測負環 ---- ### 算法核心 能不能藉由選擇走更短路,讓最後到終點的路是最短的。 ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 1 -> 2 [label="-3"] 1 -> 2 [label="-2"] } ``` ---- 有n個節點,起點最多經過n-1條路徑到終點。 ```graphviz digraph LR { rankdir=LR; a -> b [label="1"] b -> c [label="2"] c -> d [label="3"] } ``` ---- 一次迴圈至少會選擇一條路徑 如果沒有負環,經過n-1次迴圈一定會找到最短路徑 在第n次還可以選擇更短的路徑,就一定有負環 ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` | 1 | 2 | 3 | 4 | |:---:|:--------:| -------- |:--------:| | 0 | $\infty$ | $\infty$ | $\infty$ | ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1",color=Blue] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` | 1 | 2 | 3 | 4 | |:---:|:---:|:--------:|:--------:| | 0 | $1$ | $\infty$ | $\infty$ | ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1",color=Blue] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` | 1 | 2 | 3 | 4 | |:---:|:---:|:--------:|:---:| | 0 | $1$ | $\infty$ | $2$ | ---- ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1",color=Blue] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` | 1 | 2 | 3 | 4 | |:---:|:---:|:--------:|:---:| | 0 | $1$ | $\infty$ | $2$ | ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3",color=Blue] 4 -> 3 [label="-2"] } ``` | 1 | 2 | 3 | 4 | |:----:|:---:|:--------:|:---:| | $-1$ | $1$ | $\infty$ | $2$ | ---- ---- ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2",color=Blue] } ``` | 1 | 2 | 3 | 4 | |:----:|:---:|:---:|:---:| | $-1$ | $1$ | $0$ | $2$ | ---- ## 共N-1次迴圈後 ```graphviz digraph LR { rankdir=LR; 1 -> 2 [label="1"] 2 -> 4 [label="1"] 3 -> 1 [label="1"] 4 -> 1 [label="-3"] 4 -> 3 [label="-2"] } ``` | 1 | 2 | 3 | 4 | |:----:|:---:|:---:|:---:| | $-3$ | $-1$ | $-2$ | $0$ | ---- ---- ## 共N次迴圈後 | 1 | 2 | 3 | 4 | |:----:|:---:|:---:|:---:| | $-4$ | $-2$ | $-3$ | $-1$ | ---- ### 複雜度 N是節點數 V是路徑的數量 ---- ### 複雜度 N是節點數 V是路徑的數量 O(NV) --- ## 模板題 [Cycle Finding](https://cses.fi/problemset/task/1197) ---- <!-- 我先為大家示範bellmen ford做這題的前半解答 --> 1. 使用相鄰串列tuple 或是vector存每個路徑 2. 做n個節點的表 3. 從哪裡更新的表 4. 做n次尋訪,用flag 看看這次尋訪有沒有更短的路,如果最後一次沒有則NO,有則YES。 ---- 那我請大家想想如何找其中一個負環? ---- ### tuple type ```cpp= vector<long long> dist(n,(1ll<<60)); vector<long long> path_From(n,-1); dist[0] = 0; vector<tuple<int, int, long long>> vec; int a, b; long long c; for (int i = 0;i < m; ++i) { cin >> a >> b >> c; vec.push_back(make_tuple(a-1,b-1,c)); } bool flag = 0; int u, v; long long d; for (int i = 0; i < n; ++i) { flag = 0; for(auto it = vec.begin();it != vec.end();++it) { tie(u, v, d) = *it; if(dist[u]+d<dist[v]) { flag = 1; dist[v] = dist[u]+d; path_From[v] = u; } } if(!flag) { break; } } cout<<flag? "YES" : "NO"; return 0; } ``` --- ## 應用題 [High Score](https://cses.fi/problemset/task/1673) 反過來,我們要取得最大利益,就可能會有正環,要如何解決呢? 如何判斷起點到終點有沒有進入正環? ---- ## 解 我們只對能到終點與起點的點做運算 可以先用dfs或bfs掃 看看起點有經過哪些點 反過來誰可以到達終點 ---- ```cpp= void DFS(int op,vector<bool> &path,vector<vector<int>> &adj_List){ path[op] = 1; for(auto it:adj_List[op]) { if(!path[it]) { DFS(it, path, adj_List); } } } ``` ---- ```cpp= int main(){ cin.tie(0)->sync_with_stdio(0); cout.tie(0); int n, m; cin >> n >> m; // adj list long long tt = (~(1ll << 42) + 1); vector<long long> dist(n,tt); vector<tuple<int, int, long long>> tunnel(m); vector<vector<int>> adj_List(n);//adj list vector<vector<int>> bdj_list(n);//adj list vector<bool> path(m,0); vector<bool> repath(m,0); bool flag = false; for(int i=0,a,b,c; i<m; ++i) { cin >> a >> b>>c; // --a; // --b; adj_List[a-1].push_back(b-1); bdj_list[b-1].push_back(a-1); tunnel.push_back(make_tuple(a-1,b-1,c)); } DFS(0,path,adj_List); DFS(n - 1, repath,bdj_list); dist[0] = 0; for (int i = 0; i < n;++i) { flag = 0; for(auto it=tunnel.begin(); it!=tunnel.end();++it) { auto [v, u, d] = *it; if(path[v]&&repath[u]&&dist[v]+d>dist[u]) { flag = 1; dist[u] = dist[v] + d; } } } // cout << bellman_Ford()<<"\n"; if (flag) { cout << "-1\n"; } else { cout << dist[n - 1]<<"\n"; } return 0; } ``` --- ## 參考資料 [cses graph session editorial(incomplete)](https://codeforces.com/blog/entry/79518) cses題目整理 [graph_editor](https://csacademy.com/app/graph_editor/) 好畫圖工具 [Draw-Graph](http://domen111.github.io/Draw-Graph/) [印度大哥解High Score](https://cses.fi/paste/d9797aedd488129f116e2a/)
{"metaMigratedAt":"2023-06-17T14:20:40.375Z","metaMigratedFrom":"YAML","title":"圖論 負權最短距離","breaks":false,"contributors":"[{\"id\":\"e8522424-6893-41f1-959f-d5ae66fa6840\",\"add\":113998,\"del\":107589}]","description":"hackmd-github-sync-badge"}
    138 views