1. 程式人生 > >ACM模板 圖論

ACM模板 圖論

@(ACM模板)[圖論]

圖論知識點要求

必須會:
- 次短路&&路徑數
- 生成樹(看裡面的過程)
- 最大流(主要是模板)
- 割(論文:最小割模型在資訊競賽裡的應用)
- 二分圖
- 樹的分治,(點分治如重心分解,邊分治如樹鏈剖分)

瞭解,會套模板即可:
- K短路
- 度限制MST~end
- ZKW陣列模擬
- 一般圖匹配(NP,套模板)
- 各種迴路
- 瞭解經典問題

General

  1. 注意下標是0-indexed的還是1-indexed的
  2. 看好是否有重邊、自環
  3. 多組資料,vector要clear
  4. 在無向圖中,存邊的陣列的大小要開邊數的兩倍
  5. unidirectional 和 one-way都是單向邊

建圖

使用vector

鏈式前向星

  1. 設圖中n個點,m條邊。在稀疏圖(mn)中表示圖,可以用鄰接表。每個結點i有一個連結串列,儲存從i出發的所有邊。
  2. 方法:用陣列模擬連結串列:
    • 每條邊編號
    • first[u]儲存結點u的第一條邊的編號
    • next[e]表示編號為e的邊的下一條邊的編號
    • 注意每次插到連結串列的首部而非尾部,避免遍歷
  3. 程式碼實現
typedef long long LL;
const int maxn = ?;
const int maxm = ?;
int n, m;
int head[maxn], next[maxm];
struct
Edge { int to; int dis;//LL Edge(int to = 0, int dis = 0):to(to),dis(dis) {} }edges[maxm];//無向圖size為2*maxm void build_graph() { cin >> n >> m; memset(head, 0xff, sizeof head); int u, v, w; for(int i = 0; i < m; ++i) { scanf("%d%d%d", &u, &v, &w); edges[i] = Edge(u, v, w); //無向圖要加兩條
next[i] = head[u]; head[u] = i; } }

遍歷u的所有邊

for(int e = first[u]; ~e; e = next[e])
{
    //...
}

最短路

1. Dijkstra演算法

思想&步驟:
1. 初始化:d[s] = 0, 其他d為INF, 確定點集裡只有s
2. 從未被確定的點的集合裡找d最小的,加入確定的點集,並更新其他d
3. 重複2直到所有點都確定

注意:
- 不能處理負權。原因:Dijkstra貪心的找未確定點集裡d最小的,若有負權可能之後找到d更小的。
- 此為使用佇列優化的版本,複雜度O(mlog(n)),其中n為點數,m為邊數
- 基礎版執行n次,每次遍歷所有點,複雜度O(n2)
基礎版即使在n2<mlog(n)時也往往比佇列版本慢,因為佇列版本執行push操作的前提是能進行鬆弛操作,若這個式子不常常成立,則push操作會很少。

程式碼
注意:
- 若最短路是long long 的,下面的程式碼需要在相應註釋的地方更改
- 若需記錄路徑,需要啟用pa[]陣列
- 注意點的下標是否從0開始

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+5;//頂點數
const int maxm = 1e5+5;//邊數

struct Dijstra//d[t] < inf代表有解
{
    const int inf = 0x3f3f3f3f;
    //const LL inf = 0x3f3f3f3f3f3f3f3f;

    int n, m;
    bitset<maxn> done;
    int d[maxn];//LL
    int head[maxn];
    //int par[maxn];//記錄路徑

    struct Edge
    {
        int to, nxt;
        int dis;//LL
    }e[maxm];

    struct Node
    {
        int u;//結點編號
        int dis;//LL
        bool operator<(const Node &rhs) const
        {
            return dis > rhs.dis;
        }
    };

    void init(int nn)
    {
        n = nn;
        m = 0;
        memset(head, 0xff, sizeof head);
    }

    void addEdge(int from, int to, int dis)//LL dis
    {
        e[m].to = to;
        e[m].dis = dis;
        e[m].nxt = head[from];
        head[from] = m++;
    }

    void finda(int s)
    {
        priority_queue<Node> q;
        for(int i = 0; i < n; ++i) d[i] = inf;//0~n-1,注意下標從哪裡開始
        d[s] = 0;
        done.reset();
        q.push((Node){s, 0});

        while(!q.empty())
        {
            int u = q.top().u;
            q.pop();
            if(done[u] == true) continue;
            done[u] = true;
            for(int i = head[u]; ~i; i = e[i].nxt)
            {
                int v = e[i].to;
                int dis = e[i].dis;//LL
                if(d[v] > d[u] + dis)
                {
                    d[v] = d[u] + dis;
                    q.push((Node){v, d[v]});
                }
            }
        }
    }
};

2. Bellman-Ford演算法

思想&步驟
1. 初始化 所有頂點 d[i] = INF, 令d[s] = 0
2. 列舉每條邊進行鬆弛,不能鬆弛時演算法結束
3. 重複2,使2進行n-1次

memset(d, 0x3f, sizeof d);
d[s] = 0;
for(int times = 1; times < n; ++times)
{
   for(int i = 0; i < m; ++i)
   {
       int x = u[i], y = v[i];
       if(d[y] < d[x] + w[i]) d[y] = d[x] + w[i];
   }
}

3 .SPFA演算法

思想&步驟:
“鬆弛操作的連鎖反應”
用queue存可用來鬆弛的點,每次用隊首點進行鬆弛,然後將鬆弛到的&&不在queue中的點加入queue。

注意:
- 此為Bellman-Ford演算法的佇列優化版本
- 可以判斷負環。因為任意一條最短路,所經過的點不會超過n,那麼任何一點不可能超過(n-1)次被鬆弛。
- 最壞情況複雜度O(nm),但實際中往往很快

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxm = 1e5 + 5;
const int maxn = 1e5 + 5;

struct SPFA
{
    const int inf = 0x3f3f3f3f;
    //const LL inf = 0x3f3f3f3f3f3f3f3f;

    bool vis[maxn];
    int c[maxn], head[maxn];//c為入隊次數
    int d[maxn];//LL
    int n, m;

    struct Edge
    {
        int to, nxt;
        int dis;//LL
    }e[maxm];

    void init(int nn)
    {
        n = nn;
        m = 0;
        memset(head, 0xff, sizeof head);
    }

    void addEdge(int from, int to, int dis)//LL dis
    {
        e[m].to = to;
        e[m].dis = dis;
        e[m].nxt = head[from];
        head[from] = m++;
    }

    queue<int> q;
    bool finda(int s)//若存在負環返回true
    {
        memset(d, 0x3f, sizeof d);
        d[s] = 0;
        memset(vis, 0, sizeof vis);
        memset(c, 0, sizeof c);

        while(!q.empty()) q.pop();
        q.push(s);
        vis[s] = true;
        c[s] = 1;

        while(!q.empty())
        {
            int x = q.front();
            q.pop();
            vis[x] = false;
            for(int i = head[x]; ~i; i = e[i].nxt)
            {
                int y = e[i].to;
                int dis = e[i].dis;
                if(d[y] > d[x] + dis)
                {
                    d[y] = d[x] + dis;
                    if(!vis[y])
                    {
                        vis[y] = true;
                        ++c[y];
                        q.push(y);
                        if(c[y] > n) return true;
                    }
                }
            }
        }
        return false;
    }
};

4. Floyd演算法

for(int k = 0; k < n; ++k)
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j)
            if(d[i][j] < INF && d[k][j] < INF)
                d[i][j] <?= d[i][k]+d[k][j];

若距離陣列為bool,代表是否聯通,則得到的結果成為有向圖的傳遞閉包

5. 差分約束

  1. 若全部不等式均為xixjaij的形式(注意有等號),求xsxt的最值。可以建圖求最路。
  2. 對於xixjaij,建立xjxi的有向邊,權值為aij
  3. 解的存在性:
    • 存在最短路:xsxt存在最
    • 存在負環:xsxt無窮小
    • 圖中不可達,無最短路:xsxt無窮大
  4. 若全部不等式均為xixjaij的形式,求xsxt的最值。可以改為在建好的圖中求最路。
  5. 若不等式中既含有又含有,可以通過變號統一處理成,處理成還是取決於最後是求最大值還是最小值。
  6. 若含有不帶等號的不等式xixj<aij,且涉及到的都是整數,可以等價變形為xixjaij+1
  7. 最終的每個di值為xi的可行解。若{x0,x1,xn1,xn}是一組可行解,則{x0+y,x1+y,xn1+y,xn+y}也是。

6. 最長路

  1. 方法一:將原圖中的每條邊權取相反數,再用Bellman-FordSPFA求解最短路。注意不能使用Dijkstra演算法,因為其無法處理負權。
  2. 方法二:將再用Bellman-FordSPFA中的鬆弛條件反向,直接求解最長路。
  3. 再次注意不要用Dijkstra演算法
  4. 存在正環時,不再計算d[t],此時要注意圖的聯通性,即從該正環是否能走到終點。(Floyd)
    例題:poj1932

7. 次短路

對Dijkstra演算法進行修改。增加一個用於記錄次短路的陣列d2。
在進行鬆弛操作時
- 若可更新最短路,則更新最短路,原最短路變為次短路
- 若可更新次短路,則更新次短路

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 5;//頂點數
const int maxm = 1e5 + 5;//邊數
const int inf = 0x3f3f3f3f;
//const LL inf = 0x3f3f3f3f3f3f3f3f;

struct Dijstra//d2[t] < inf代表有解
{

    int n, m;
    int d[maxn], d2[maxn];//LL,d記錄最短路,d2記錄次短路
    int head[maxn];
    //int par[maxn];//記錄路徑

    struct Edge
    {
        int to, nxt;
        int dis;//LL
    }e[maxm];

    struct Node
    {
        int u;//結點編號
        int dis;//LL
        bool operator<(const Node &rhs) const
        {
            return dis > rhs.dis;
        }
    };

    void init(int nn)
    {
        n = nn;
        m = 0;
        memset(head, 0xff, sizeof head);
    }

    void addEdge(int from, int to, int dis)//LL dis
    {
        e[m].to = to;
        e[m].dis = dis;
        e[m].nxt = head[from];
        head[from] = m++;
    }

    void finda(int s)
    {
        priority_queue<Node> q;
        for(
            
           

相關推薦

ACM模板

@(ACM模板)[圖論] 圖論知識點要求 必須會: - 次短路&&路徑數 - 生成樹(看裡面的過程) - 最大流(主要是模板) - 割(論文:最小割模型在資訊競賽裡的應用) - 二分圖 - 樹的分治,(點分治如重心分解,

ACM基本演算法詳解

圖論基本演算法 DFS,BFS 兩個生成樹prim + Kruskal 4個最短路徑Dijkstra+Floyd+Bellman-Ford+SPFA DFS&BFS DFS——遍歷所有解 模板: void DFS( Point

洛谷P3385 【模板】負環 DFS-SPFA 判負環

string inf scan space can 清空 span %d pre 洛谷P3385 【模板】負環 圖論 今天get了 一個 DFS-SPFA 判負環的方法 一般的 BFS-SPFA 判負環 一般就是 不停地做,如果某點第 n+1次加入隊列中,那麽說明這個圖存在

算法-Tarjan模板 【縮點;割頂;雙連通分量】

else if false -m 例如 als for 算法思路 連通 tarjan 圖論算法-Tarjan模板 【縮點;割頂;雙連通分量】 為小夥伴們總結的Tarjan三大算法 Tarjan縮點(求強連通分量) int n; int low[100010],dfn[1

題目模板,和並查集:以後的題目就靠他了

fat union 一次 情況 返回 end empty 是我 min ‘‘‘ 並查集: 1.用於查如何A,B是否在一個集合中. 2.每一個集合設立一個頭結點.其他都連向他 3.集合合並就是把小的集合掛到大的集合下面即可 4.優化.查詢到一個a在b這個頭結點下面,那麽直

noip提高組模板

//拓撲排序 void Top(){ for(i=1;i<=n;++i) if(!du[i]) q.push(i); while(!q.empty()){ int u=q.front();q.pop(); ans[++num]=u; for(int i=head[u];i;

NOIP複賽複習(五)程式對拍與模板

程式對拍 所謂“對拍”,顧名思義,就是讓兩者相互比對。所謂“兩者”,一是你要測試的程式,二是一個答案在該程式在一定範圍(時間/空間)內結果必定正確的程式(一般是用暴力求解的程式)。對拍一般需要造資料程式(data.exe),保證正確性的暴力對拍程式(test.exe)與測試程式(以moo.e

模板

線段樹優化 cpp 算法 圖論 n) while uil ont 堆優化 基礎圖論 鏈式前向星 帶權值 int head[400001],ver[2000001],nxt[2000001],val[2000001],tot=0; void add(int x,int y,i

幹貨系列——模板1

class con ·· spfa算法 幹貨 jks true 鏈式 dijk 圖論常用模板: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 圖的建立

複習1-模板

1.最短路 圖全為正權使用Dijkstra,有負權用SPFA,Bellman-Ford稍加了解即可 void spfa(){ queue<int> q; for(int i = 1;i <= n;i++) d[i] = 0x7fffffff;

演算法模板

Under the bridge downtown Forgot about my love Under the bridge downtown I gave my life away Luogu P4779【模板】單源最短路徑(標準版) //時間複雜度O((n+m)log

模板(2)

Tarjan演算法 割點(割頂) #include<cstdio> #include<cstring> #include<algorithm> #define _ 200010 using namespace std; struct node{

模板(1)

最短路(ShortextPath) Dijkstra #include<bits/stdc++.h> using namespace std; /*Dijkstra演算法*/ int a[3010][3010],d[3010],n,m; bool v[3010];

模板(3)

一筆畫問題(尤拉路) DFS版 /*1.一筆畫問題 *規定 所有的邊都只能畫一次,不能重複畫 *輸入 第一行只有一個正整數N(N<=10)表示測試資料的組數. 每組測試資料的第一行有兩個正整數P,Q(P<=1000,Q<=2000), 分別表示這個畫中有多

模板庫(三) - 演算法模板

寫在前面 “模板庫”這一系列文章用來複習 O I OI

-環-洛谷P3385【模板】負環

這道題有毒啊。。輸出的不是“NO”是“N0”,不是“YES”而是“YE5”。被坑了一晚上。 另外,spfa-dfs竟然被卡死了,只能過9個點。換成三行就寫完的Bellmam-Ford就AC了 SPFA-

:HDU-4009 Transfer water (最小樹形圖模板

XiaoA lives in a village. Last year flood rained the village. So they decide to move the whole village to the mountain nearby this year. T

】單源點最短路模板(有向)Dijkstra

#include <cstdio> #include <iostream> #include <cstring> #include <queue> #in

NOIP模板複習——

由於圖論中有些演算法的程式碼比較長,就只貼核心程式碼 最短路 floyd void floyd() { int i,j,k; for(k=1;k<=n;++k) for(i=1;i<=n;++i) for(j=1;j<=n;++j

ACM__有向的強連通分支演算法

求有向圖的強連通分支,主要有兩種演算法tarjan演算法和kosaraju演算法,這裡介紹tarjan演算法先來看幾個定義:(1)連通:兩個點可以相互到達(2)強連通(strongly connected): 在一個有向圖G裡,設兩個點 a b 發現,由a有一條路可以走到b,