求最短路的三種方法:dijkstra,spfa,floyd

来源:http://www.cnblogs.com/DearDongchen/archive/2017/05/26/6910164.html
-Advertisement-
Play Games

一些事情久久不能釋懷,於是最近學習了下最短路的演算法,希望我能變得輕鬆些。 dijkstra是一種單源最短路演算法。在沒有負權值的圖上,vi..vj..vk是vi到vk最短路的話,一定要走vi到vj的最短路。所以每次取出到起點距離最小的點,從該點出發更新鄰接的點的距離,如果更新成功則把新點加入prior ...


一些事情久久不能釋懷,於是最近學習了下最短路的演算法,希望我能變得輕鬆些。

dijkstra是一種單源最短路演算法。在沒有負權值的圖上,vi..vj..vk是vi到vk最短路的話,一定要走vi到vj的最短路。所以每次取出到起點距離最小的點,從該點出發更新鄰接的點的距離,如果更新成功則把新點加入priority_queue。儲存圖使用的是鄰接表。代碼如下:

#include <bits/stdc++.h>//有向圖
using namespace std;
//dijkstra 不適用於帶負權的圖
const int maxn = 1007, maxm = 20007, INF = 2147483467;// maxn比頂點數略大,maxm比邊數略大
int head[maxn], vis[maxn], dis[maxn], fa[maxn];
int next[maxm], u[maxm], v[maxm], w[maxm];
typedef pair<int, int> pi;// pair排序策略是優先排前面,pair把距離放前面,點放後面
int n, m;
void ini()
{
    memset(vis, 0, sizeof(vis));//開始所有點都沒被處理
    memset(head, -1, sizeof(head));//所有點都沒有邊
    for(int i = 0; i <= n; i++)
        dis[i] = INF;//起點到所有點距離為無窮大
}

void dij(int s, int e)
{
    priority_queue<pi> Q;//優先隊列
    dis[s] = 0;
    Q.push(make_pair(dis[s], s));
    while(!Q.empty())
    {
        pi u = Q.top();
        Q.pop();//取出d最小的點
        int x = u.second;
        if(x == e)
            break;
        if(vis[x])//處理過,跳過
            continue;
        vis[x] = 1;
        for(int i = head[x]; ~i; i = next[i])//更新從當前d最小的點相鄰點的距離
        {
            if(dis[v[i]] > dis[x] + w[i])
            {
                dis[v[i]] = dis[x] + w[i];//鬆弛成功
                fa[v[i]] = x;
                Q.push(make_pair(dis[v[i]], v[i]));//把更新成功的點加入隊列
            }
        }
    }
    if(dis[e] == INF)
        return;//s到e無路徑
    vector<int> ans;//用vector從終點往起點找路徑,也可以用遞歸
    int temp = e;
    while(temp!=s)
    {
        ans.push_back(temp);
        temp = fa[temp];
    }
    ans.push_back(s);
    for(int i = ans.size()-1; i >= 0; i--)
        printf("%d ", ans[i]);
    printf("\n%d\n", dis[e]);
}

int main()
{
    //freopen("in.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        ini();
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", u+i, v+i, w+i);
            next[i] = head[u[i]];
            head[u[i]] = i;//建立鄰接表
        }
        int S, E;//起點終點
        scanf("%d%d", &S, &E);
        dij(S, E);
    }
    return 0;
}

dijkstra經典的一比,不過要求不能含負權,於是又學了下能處理帶負權圖的spfa:

spfa感覺有點像bfs,但bfs只處理一個節點一次,而spfa如果鬆弛了路徑上經過的節點,就要對路徑上之後的點都更新一遍。有負環的話,每走一遍負環,距離就減小環長,也就不存在最短路,所以假定不存在負環(或者判斷一下有無負環)。代碼如下:

#include <bits/stdc++.h>//有向圖
using namespace std;
const int maxn = 1007, maxm = 10007, INF = 2147483467;
int head[maxn], in[maxn], dis[maxn], fa[maxn];//in標記在queue中的點
int u[maxm], v[maxm], w[maxm], next[maxm];
int n, m;

void ini()
{
    memset(in, 0, sizeof(in));
    memset(head, -1, sizeof(head));
    for(int i = 0; i <= n; i++)
        dis[i] = INF;
}

void print(int s, int e)//遞歸列印路徑
{
    if(e!=s)
        print(s, fa[e]);
    printf("%d ", e);
}

void spfa(int s, int e)//有點像bfs,不過bfs不會處理之前處理過的點
{
    queue<int> Q;
    dis[s] = 0;
    in[s] = 1;
    Q.push(s);
    while(!Q.empty())
    {
        int x = Q.front();
        Q.pop();
        in[x] = 0;
        for(int i = head[x]; ~i; i = next[i])
        {
            if(dis[v[i]] > dis[x] + w[i])
            {
                dis[v[i]] = dis[x] + w[i];
                fa[v[i]] = x;
                if(!in[v[i]])
                   {
                       Q.push(v[i]);
                       in[v[i]] = 1;
                   }
            }
        }
    }
    print(s, e);
    printf("\n%d\n", dis[e]);
}
int main()
{
    freopen("in.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        ini();
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", u+i, v+i, w+i);
            next[i] = head[u[i]];
            head[u[i]] = i;
        }
        int s, e;
        scanf("%d%d", &s, &e);
        spfa(s, e);
    }
    return 0;
}

多源最短路徑有個floyd演算法,其實就是離散講的warshall閉包:(但是因為時間複雜度O(n^3)有點嫌棄2333),沒自己實現一下,核心代碼如下:

    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                if(g[i][j] > g[i][k] + g[k][j])
                    g[i][j] = g[i][k] + g[k][j];

思路是開始用鄰接矩陣存放圖,更新兩點距離只能靠經過其他點中轉,所以每次多允許經過一個點(即第一個迴圈),考慮最短路(第二三重迴圈)。

floyd好處是代碼短,能一次求出所有節點之間的最短路。

 

今天收穫挺大,總結一下稠密/無負權圖用dijkstra,稀疏/有負權圖用spfa,時間要求不高用floyd。

 

 

 

(心情依然很爛)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題目 闡述創建線程最常用的兩種方法及其對比。 解答 方法一:繼承Thread類實現 步驟: 方法二:實現Runnable介面 步驟: 繼承Thread類創建線程與實現Runnable介面創建線程的不同之處在於,當用同一個類創建多個線程的時候,前者實際上是創建了多個不同的Thread對象,它內部的ru ...
  • 轉自http://www.cnblogs.com/myit/p/4269165.html 我感覺Dbutils用的最多的就是對查詢結果集的處理,就以這個開始瞭解Dbutils庫。 查看源代碼發現結果集的轉換主要用於query,insert,insertBatch方法。 對ResultSet的轉換主要 ...
  • 作業 1, ATM:模擬實現一個ATM + 購物商城程式額度 自定義實現購物商城,買東西加入 購物車,調用信用卡介面結賬可以提現,手續費5%支持多賬戶登錄支持賬戶間轉賬記錄每月日常消費流水提供還款介面ATM記錄操作日誌提供管理介面,包括添加賬戶、用戶額度,凍結賬戶等。。。用戶認證用裝飾器程式結構:A... ...
  • 1、類載入子系統:負責從文件系統或者網路中載入Class信息,載入的信息存放在一塊稱之為方法區的記憶體空間。 2、方法區:就是存放類信息、常量信息、常量池信息、包括字元串字面量和數字常量等。方法區是輔助堆棧的塊永久區,解決堆棧信息的產生,是先決條件。 3、Java堆:再java虛擬機啟動的時候建立Ja ...
  • GetConsoleTitle函數 來源:https://msdn.microsoft.com/en us/library/windows/desktop/ms683174(v=vs.85).aspx 作用 獲取當前控制台視窗的標題 語法 參數 lpConsoleTitle 用於保存控制台標題。若設 ...
  • std::string s1 = R"(Name="Hello World ... ")"; std::string s2 = R"-(Name="(Hello World ... )")-"; std::string s3 = R"-(Name="Hello World ... ")-"; std... ...
  • 在java里, 我們可以使用Executors.newFixedThreadPool 來創建線程池, 然後就可以不停的創建新任務,並用線程池來執行了。 在提交任務時,如果線程池已經被占滿,任務會進到一個隊列里等待執行。 這種機制在一些特定情況下會有些問題。今天我就遇到一種情況:創建線程比線程執行的速 ...
  • hljs.initHighlightingOnLoad(); SetConsoleScreenBufferSize函數 來源:https://msdn.microsoft.com/en us/library/windows/desktop/ms686044(v=vs.85).aspx 作用 指定控制 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...