Eric
    • Create new note
    • Create a note from template
      • 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
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • 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
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
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
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • 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
    # 最短路徑演算法 ## All-Pairs Shortest Path ### Floyd-Warshall algorithm 不可用於有負邊的圖 1. 初始化 ```cpp= for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j) dis[i][j]=0; //把自己到自己設成0 else dis[i][j]=inf; //把所有點之間設成無限大 } } //輸入時要注意取最小值 for(int i=0,u,v,w;i<m;i++){ cin >> u >> v >> w; if(w<dis[u][v]){ dis[u][v]=w; dis[v][u]=w; } } ``` 可以簡化成在宣告的時候就初始為無限大 ```cpp= vector<vector<int>> Map(n,vector<int>(n,INT_MAX)); for(int i=0;i<n;i++) Map[i][i]=0; ``` 2. 利用dp動態規劃,對於每個點可以是直接抵達(i->j),或者經過地圖上任意一個點(k)抵達(i->k->j),兩者取最小值即為兩個點間的最小距離 ```cpp= dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]); ``` 三層迴圈窮舉i j k 注意:中繼站k要在最外層 ```cpp= for(int k=0;k<n;k++){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ Map[i][j]=min(Map[i][j],Map[i][k]+Map[k][j]); } } } ``` 這樣就能得到任兩個點(i,j)之間的最短距離 ## Single-source Shortest Path ### Dijkstra Algorithm(邊權非負) 可用於有向圖與無向圖,但邊權不能是負的 用於求單點對多點的最短距離,有點像最小生成樹的prim演算法 1. 在還未被設為確定值的元素中找距離起點的最小值 2. 將找到的節點設為確定值,因為不管怎麼繞路都不會有更小的值了 3. 更新此點可走到的其他點 ```cpp= int Dijkstra (int s,int e){ vector<int> dis(n,1e9); dis[s]=0; vector<bool> confirm(n,false); confirm[s]=true; for(int i=0;i<n;i++){ //反覆尋找與起點最短距離的點,最多n個 int cur=-1; for(int j=0;j<n;j++){ if(!confirm[j]){ //僅找未被更新的元素 if(cur==-1 || dis[j]<dis[cur]){ //找最小值 cur=j; } } } if(cur==-1){ break; //此圖剩下的節點走不到 } confirm[cur]=true; //設為確定值 for(auto j:Map[cur]){ //更新此節點可走的到的節點的離起點最小值 int d=value[cur][j]; if(!confirm[j]){ //僅更新未被確定的元素 if(dis[cur]+d<dis[j]){ //更新最小值 dis[j]=dis[cur]+d; } } } } if(dis[e]==1e9) cout << "Impossible to reach"; else cout << dis[e]; } ``` 與prim不同的地方在於最後更新的d的地方,Dijkstra要更新起點到j的距離,prim則是更新最小生成數到j的距離 prim: 判斷有沒有在最小生成樹裡的集合 ```cpp= //d=從最小生成樹到j的最小距離 for(int j=0;j<n;j++){ if(!visit[j] && map[index][j]<d[j]){ d[j]=map[index][j]; } } ``` Dijkstra: 算路徑長 ```cpp= //d=從key_poinr到j的最小距離 for(int j=1;j<=n;j++){ if(map[index][j]!=1e9 && d[index]+map[index][j]>d[j]){ d[j]=map[index][j]+d[index]; } } ``` #### priority_queue 在求最小距離時可用priority queue優化 ```cpp= #define pii pair<int,int> #define val first #define idx second //Map[x].push_back({value,y}); //從x走到y權重為value priority_queue<pii,vector<pii>,greater<pii>> pq; //以距離排序所以first要放起點到該節點的距離,second才放編號 pq.push({0,s}); vector<int> dis(n,1e9); dis[s]=0; while(!pq.empty()){ pii cur=pq.top(); pq.pop(); if(dis[cur.idx]<cur.val) continue; //如果到當前節點的value比dis[當前節點]大 //代表有其他邊可以以更小的value走到到此節點 //那此節點因為比較大就不更新 for(auto i:Map[cur.idx]){ if(dis[cur.idx]+i.val<dis[i.idx]){ //更新到i.first的最小值 dis[i.idx]=dis[cur.idx]+i.val; pq.push({dis[i.idx],i.idx}); } } } ``` set寫法+二維陣列 ```cpp= //ZJ d793 路徑最小權重和 int dx[4]={0,1,0,-1}; int dy[4]={1,0,-1,0}; int a[MAXN][MAXN]; int dis[MAXN][MAXN]; bool used[MAXN][MAXN]; struct node{ int val,x,y; }; bool operator<(const node a,const node b){ //運算子重載,給set排序function return a.val<b.val; } multiset<node> pq; //注意:要multiset,因為在過程中有可能有到此點的更小距離出現但如果用set就會 pq.insert({a[0][0],0,0}); dis[0][0]=a[0][0]; while(!pq.empty()){ node now=*pq.begin(); pq.erase(pq.begin()); if(now.val>dis[now.x][now.y]) continue; for(int i=0;i<4;i++){ int tx=now.x+dx[i],ty=now.y+dy[i]; if(tx>=n || tx<0 || ty>=m || ty<0) continue; if(now.val+a[tx][ty]<dis[tx][ty]){ dis[tx][ty]=now.val+a[tx][ty]; pq.insert({dis[tx][ty],tx,ty}); } } } cout << dis[n-1][m-1] << '\n'; ``` ### Bellman-Form 也是求單源最短路徑,但可用於負邊,進而判斷是否有負環 枚舉每條邊(from a to b,權重:v),如果從a經過這條邊走到b會小於原其他路走到b,那就更新distance[b]=distance[a]+v; 概念是DP,轉移式如下 ```cpp= dp[k][b]=min(dp[k][b],dp[k-1][a]+value[a][b]); ``` dp[k][i]代表從起點走k條邊的情況下,走到i的最短距離 1. 最短距離一定是<=n-1條邊,如果>n-1的話就代表有負環,因為只要能再多接一條邊就是繞回去先前已算過的點,這樣就會形成無止盡的負環 2. 最多算n-1次,由小到大,dp轉移式可省略k,因為每次都是執行完k-1後才會到k 綜合以上兩點可以先寫出一份Code ```cpp= struct node{int a,b,c;}; vector<node> Map; //紀錄路 from a to b ,權重:c vector<int> dis(n,1e9); //紀錄起點到index的最短距離 dis[0]=0; //初始化設起點到起點為0 for(int v=0;v<n-1;v++){ //最短距離最多有n-1條邊 for(auto i:Map){ //枚舉每一條邊 if(dis[i.a]+i.v<dis[i.b]){ //如果從i這條邊到結點b會比其他邊到b小的話 dis[i.b]=dis[i.a]+i.v; } } } ``` 再執行一次即可判斷有無負環,最外層的迴圈也不用了 ```cpp= bool negative_circle=false; for(auto i:Map){ if(dis[i.a]+i.v<dis[i.b]){ negative_circle=true; break; } } ``` ### SPFA 全名shortest path faster algorithm 有兩個優點 1. Dijkstra演算法的優化: 在進入queue時先判斷此點是否已在queue裡,如果沒有的話才加入,可省掉相同元素,在出隊後的判斷。入隊的時候,檢查此元素的dis是否小於當前queue的頭的dis,基於greedy,如果有就插入隊伍最前面,否則插入最後面 2. 可判斷負環: 當一個點進入queue的次數>=n(節點數)時,代表被更新了n次,即有負環,因為如果沒有負環的話,經過此點的次數最多只會是n-1(扣除自己的其他節點),不可能再更小,除非有負環 ```cpp= vector<int> dis(n,inf),cnt(n,0); vector<bool> inq(n,false); //紀錄此元素是否在queue裡 list<int> q{start}; inq[start]=true; //紀錄進入queue裡 dis[start]=0; while(!q.empty()){ int now=q.front();q.pop_front(); inq[now]=false; //紀錄移出queue for(auto i:Map[now]){ if(dis[now]+i.S<dis[i.F]){ dis[i.F]=dis[now]+i.S; if(!inq[i.F]){ //如果沒有在queue裡才加入 cnt[i.F]++; if(cnt[i.F]==n) cout << "negative circle" << '\n'; //有負環 if(!q.empty() && dis[i.F]<dis[q.front()]) q.push_front(i.F); //如果比前面小就加入前面 else q.push_back(i.F); //否則加入後面 inq[i.F]=true; //紀錄進入queue } } } } ``` ### 拓譜排序(DAG) 可同時求最大和最小路徑 利用拓譜排序性質=>走到此點的點都被走過才會走此點 進而更新此點的最短距離 ```cpp dis[v]=min(dis[v],dis[u]+val(u,v)) ``` ```cpp= for(int i=0;i<n;i++) dis[0][i]=1e9; for(int i=0;i<n;i++) dis[1][i]=-1e9; dis[0][s]=0; dis[1][s]=0; queue<int> q; for(int i=0;i<n;i++) if(task[i]==0) q.push(i); while(!q.empty()){ int now=q.front();q.pop(); for(auto j:Map[now]){ int i=j.first; dis[0][i]=min(dis[0][i],dis[0][now]+j.second); dis[1][i]=max(dis[1][i],dis[1][now]+j.second); task[i]--; if(task[i]==0){ q.push(i); } } } ``` ### 求有向圖的且有負邊的最短路徑 如果是無向圖可利用Bellman-Form,因為一個連通圖可以隨意走,只要有負環就可以一直走負環,但有向圖就不一定有一條路徑是通往負環的,所以要檢查負環是否在起點到終點的路徑上,可利用正反兩次dfs求得起點到終點的路徑,再檢查負環是否在路徑上 ```cpp= void dfs(int now,int idx){ vis[idx][now]=true; for(auto i:Map[idx][now]){ if(!vis[idx][i]) dfs(i,idx); } } //find the path through starting point and end point,which is vis[0][i] && vis[1][i] dfs(1,0),dfs(n,1); vector<int> dis(n+1,inf); dis[1]=0; //Bellman-Form for(int i=0;i<n-1;i++){ for(auto [a,b,c]:road){ if(dis[a]+c<dis[b]) dis[b]=dis[a]+c; } } //check whether the negative edge is connected to the path which is through starting point and end point for(auto [a,b,c]:road){ if(dis[a]+c<dis[b] && vis[0][b] && vis[1][b]){ cout << -inf << '\n'; return; } } cout << dis[n] << '\n'; ``` UVA 558 (Bellman-Form) ```cpp= #include <bits/stdc++.h> using namespace std; struct edge{int a,b,d;}; bool bellman_ford(vector<edge> &Map,int n){ vector<int> dis(n,1e9); dis[0]=0; for(int v=0;v<n-1;v++){ for(auto i:Map){ if(dis[i.a]+i.d<dis[i.b]){ dis[i.b]=dis[i.a]+i.d; } } } for(auto i:Map){ if(dis[i.a]+i.d<dis[i.b]){ return true; } } return false; } int main(){ cin.sync_with_stdio(0),cin.tie(0); int n,m,t,x,y,d; cin >> t; while(t--){ cin >> n >> m; vector<edge> Map; for(int i=0;i<m;i++){ cin >> x >> y >> d; Map.push_back({x,y,d}); } if(bellman_ford(Map,n)) cout << "possible\n"; else cout << "not possible\n"; } } ```

    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