1. 程式人生 > >圖的最短路徑及最小生成樹 模板

圖的最短路徑及最小生成樹 模板

/*
3
0 1
0 2
1 2
-1 -1

NO

4
0 1
0 3
1 2
2 3
-1 -1
YES
*/
#include <iostream>
#include<vector>
#include<cstring>
using namespace std;
#define maxv 1000
vector<int> G[maxv];//圖
int V;//頂點數
int color[maxv];//頂點i的顏色
//將頂點染成1或-1
bool dfs(int v,int c)
{
    color[v]=c;//把頂點v染成顏色c
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        //相鄰頂點同色,返回false
        if(color[u]==c) return false;
        //相鄰頂點還未染色,染成-c
        if(!color[u]&&!dfs(u,-c)) return false;
    }
    //如果所有的頂點都染過色了,則返回true
    return true;
}
void solve()
{
    for(int i=0;i<V;i++){
        if(color[i]==0){
            //頂點i未被染色,染成1
            if(!dfs(i,1)){
                cout<<"NO\n"<<endl;
                return;
            }
        }
    }
    cout<<"YES\n"<<endl;
}
int main()
{
    cin>>V;
    int u,v;
    cin>>u>>v;
    while(u>=0&&v>=0){
        G[u].push_back(v);
        G[v].push_back(u);
        cin>>u>>v;
    }
    memset(color,0,sizeof(color));
    solve();
    return 0;
}

2.最短路徑問題

最短路是給定兩個頂點,在以這兩個頂點為起點和終點的路徑中,邊的權值之和最小的路徑。智力遊戲中的最小步數,也可以看作最短路徑問題。

1.單源最短路徑1(Bellman-Ford演算法)

1.演算法原理

記從起點s出發到頂點i的最短距離為d[i],則有d[i]=min{d[j]+(從j到i的邊的權值)|e=(j,i)屬於E}

如果給定的圖是DAG,就可以按拓撲序給頂點編號,按照上述遞推關係式計算出d。但是圖中如果有圈,就不可以依賴這樣的順序計算。在這種情況下,設初值d[s]=0,d[i]=INF(足夠大的常數),在不斷利用這個遞推關係式就可以解決。只要圖中不存在負圈,操作就是有限的。

2.程式碼及樣例:

/*
5 10
0 1 6
0 3 7
1 2 5
1 3 8
1 4 -4
2 1 -2
3 2 -3
3 4 9
4 0 2
4 2 7

0 2 4 7 -2
*/
#include <iostream>
#include<vector>
#include<cstring>
using namespace std;
#define maxn 1000
#define INF 0x7fffffff
int V,E;
struct edge
{
    int from;
    int to;
    int cost;
};
edge es[maxn];//邊
int d[maxn];//最短距離
//求解從頂點s出發到所有頂點的最短距離
void shortest_path(int s)
{
    for(int i=0;i<V;i++){
        d[i]=INF;
    }
    d[s]=0;
    while(true){
        bool update=false;
        for(int i=0;i<E;i++){
            edge e=es[i];
            int u=e.from,v=e.to;
            if(d[u]!=INF&&d[v]>d[u]+e.cost){
                d[v]=d[u]+e.cost;
                update=true;
            }
        }
        if(!update) break;
    }
}
int main()
{
    cin>>V>>E;
    for(int i=0;i<E;i++){
        int u,v,c;
        cin>>u>>v>>c;
        es[i]=(edge){u,v,c};//這個感覺好6,以前沒見過……
    }
    shortest_path(0);
    for(int i=0;i<V;i++){
        cout<<d[i]<<" ";
    }
    cout<<endl;
    return 0;
}

3.時間複雜度分析

如果圖中不存在從s可達的負圈,那麼最短路不會經過一個頂點兩次(也就是說最多通過|V|-1條邊,while迴圈最多執行|V|-1次。如果存在從s可達的負圈,那麼第|V|次迴圈也會更新d的值,可以用這個性質,檢查負圈。如果一開始把d[i]全部初始化成0,就可以檢查所有的負圈。

4.負圈檢查

//檢查是否存在負圈
bool find_negative_loop()
{
    memset(d,0,sizeof(d));
    for(int i=0;i<V;i++){
        for(int j=0;j<E;j++){
            edge e=es[i];
            if(d[e.to]>d[e.from]+e.cost){
                d[e.to]=d[e.from]+e.cost;
                //如果第n次仍然更新了,則存在負圈
                if(i==V-1) return false;
            }
        }
    }
    return true;
}

3.最小生成樹

2.Kruscal演算法

1.演算法原理

Kruscal演算法按照邊的權值的順序從小到大檢視一遍,如果不產生圈,就把這條邊加入到生成樹中。

現在介紹如何判斷是否產生邊。假設現在要把連線頂點u和v的邊e加入到生成樹中,如果之前u和v不在同一個連通分量中,那麼加入e不會產生圈,否則會。可以用並查集高效地判斷是否屬於同一個連通分量。

Kruscal演算法在邊的排序上最花時間,演算法的時間複雜度為O(ElogV)。

2.程式碼模板

/*
9 14
0 1 4
0 7 8
1 2 8
1 7 11
2 3 7
2 5 4
2 8 2
3 4 9
3 5 14
4 5 10
5 6 2
6 7 1
6 8 6
7 8 7

37
*/
#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
#define maxn 1005
int par[maxn];//父親
int Rank[maxn];//樹的高度
//初始化
void init(int n)
{
    for(int i=0;i<n;i++){
        par[i]=-1;
        Rank[i]=0;
    }
}
//查詢父親,路徑壓縮
int Find(int x)
{
    int s;
    for(s=x;par[s]!=-1;s=par[s]);
    while(s!=x){
        int temp=par[x];
        par[x]=s;
        x=temp;
    }
    return s;
}
//合併x,y屬於的集合
void unite(int x,int y)
{
    x=Find(x);y=Find(y);
    if(x==y) return;
    if(Rank[x]<Rank[y]){
        par[x]=y;
    }
    else{
        par[y]=x;
        if(Rank[x]==Rank[y]) Rank[x]++;
    }
}
bool same(int x,int y)
{
    return Find(x)==Find(y);
}
struct edge
{
    int from;
    int to;
    int cost;
} ;
bool cmp(const edge& e1,const edge& e2)
{
    return e1.cost<e2.cost;
}
int V,E;
edge es[maxn];
int kruscal()
{
    sort(es,es+E,cmp);//按照edge.cost從小到大的順序進行排序
    init(V);
    int res=0;
    for(int i=0;i<E;i++){
        edge e=es[i];
        if(!same(e.from,e.to)){
            unite(e.from,e.to);
            res+=e.cost;
        }
    }
    return res;
}
int main()
{
    cin>>V>>E;
    for(int i=0;i<E;i++){
        int u,v,c;
        cin>>u>>v>>c;
        es[i]=(edge){u,v,c};
    }
    printf("%d\n",kruscal());
    return 0;
}

相關推薦

路徑小生成樹 模板

/* 3 0 1 0 2 1 2 -1 -1 NO 4 0 1 0 3 1 2 2 3 -1 -1 YES */ #include <iostream> #include<vector> #include<cstring> using namespace std; #d

秋季學期一起開心講課-week06-路徑小生成樹,二分,存方式

存圖的兩種方式: 1.鄰接矩陣 #include<iostream> #include<cstirng> using namespace std; #define maxn 1000 int mat[maxn][maxn]; int main() { //初始化

論經典演算法(通俗易懂):路徑小生成樹

一、最短路問題 求圖的最短路問題,幾乎是圖論的必學內容,而且在演算法分析與設計中也會涉及。很多書上內容, 實在沒法看,我們的圖論教材,更是編的非常糟糕,吐槽,為啥要用自己學校編的破教材,不過據說 下一屆終於要換書了。 言歸正傳,開始說明最短路問題。

(有向,無向)的鄰接矩陣表示C++實現(遍歷,拓撲排序,路徑小生成樹) Implement of digraph and undigraph using adjacency matrix

本文實現了有向圖,無向圖的鄰接矩陣表示,並且實現了從建立到銷燬圖的各種操作。 以及兩種圖的深度優先遍歷,廣度優先遍歷,Dijkstra最短路徑演算法,Prim最小生成樹演算法,有向圖的拓撲排序演算法。 通過一個全域性變數控制當前圖為有向圖還是無向圖。 若為無向圖,則生成的

#路徑小生成樹#CH 6202 黑暗城堡

題目 求邊都在從1開始的最短路徑上的最小生成樹的個數 分析 那麼也就是說,首先先跑一遍最短路,用乘法原理求出答案 程式碼 #include <cstdio> #include <cctype> #include <cstrin

How Many Maos Does the Guanxi Worth(無向路徑大值)

Guanxi" is a very important word in Chinese. It kind of means "relationship" or "contact". Guanxi can be based on friendship, but also can be built on

[Vijos 2024]無向路徑短路)

https://vijos.org/p/2024 題意: 給你圖中每兩個點的最短路,問你可不可以增加一條邊的權值,使最小值不受影響,讓這個最大值最大。 思路:一個圖已經給定了,怎樣才能增加一個邊的權值使他最小值不受影響呢,應該可以想到就是當一個點可以被鬆弛的時候 我們可以

論演算法:路徑——無權路徑演算法和Dijkstra演算法C++實現

前言        今天將給大家介紹的是圖論演算法中的另外一個基礎部分——最短路徑演算法;其中又分為無權最短路徑,單源最短路徑,具有負邊的最短路徑以及無圈圖等;而這次將介紹常見的兩個——無權最短路徑以及單源最短路徑。接下來就開始我們的講解吧~~原理        最短路徑演算

已知n行資料,自上而下為第1行,第2行.....第n行。第i行資料有i個,求從第1行至第n行的路徑路徑長度。

#include <stdio.h> //主函式 void main(){int a[50][50];//儲存資料的陣列int value[50][50];//儲存計算後的值int sataus[50][50];//儲存分支的值int min;//儲存最後一行中

Matlab計算路徑路徑的個數

    最近老闆讓計算最短路徑及路徑個數,找遍了所有工具箱,都沒現成的。急死了,什麼Dijkstra和Floyd都搞不定。最後,想了想,算了吧,自己編吧,反正自己用,又沒有演算法複雜度要求。於是自己就寫了個小程式(本程式僅限無權無向連通圖),演算法複雜度不曉得()。    

單源路徑短路)

ext getchar 路徑 鄰接鏈表 單源最短路 fin struct true com 洛谷——P3371 【模板】單源最短路徑(spfa) 題目描述 如題,給出一個有向圖,請輸出從某一點出發到所有點的最短路徑長度。 輸入輸出格式 輸入格

路徑路徑問題

導入 n+2 lan ble 一行 memset ems esp php [提交] [狀態] [討論版] [命題人:外部導入] 題目描述 平面上有n個點(n<=100),每個點的坐標均在-10000~10000之間。其中的一些點之間有連線。 若有連線,

Dijkstra求短路的條數,並輸出路徑短路經過的點的大和

#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <stack> using name

資料結構--C語言--的深度優先遍歷,廣度優先遍歷,拓撲排序,用prime演算法實現小生成樹,用迪傑斯特拉演算法實現關鍵路徑和關鍵活動的求解,路徑

實驗七  圖的深度優先遍歷(選做,驗證性實驗,4學時) 實驗目的 熟悉圖的陣列表示法和鄰接表儲存結構,掌握構造有向圖、無向圖的演算法 ,在掌握以上知識的基礎上,熟悉圖的深度優先遍歷演算法,並實現。 實驗內容 (1)圖的陣列表示法定義及

小生成樹 VS 路徑

圖的應用問題 note 僅作入門參考的記錄 1. 網路架設之路徑最短問題 參考給出了兩個演算法:Prim 演算法和Kruskal演算法,前者針對鄰接矩陣,後者針對邊集陣列。

(Graph)——小生成樹路徑、Kruskal、Dijkstra、Floyd

4. 最小生成樹 4.1 生成樹 (1)定義:所有頂點均由邊連線在一起,但不存在迴路的圖叫該圖的生成樹 (2)深度優先生成樹與廣度優先生成樹 (3)     一個圖可以有許多棵不同的生成樹    所有

資料結構:——的遍歷、小生成樹路徑演算法

前言 在這裡,如果大家對圖或者資料結構還不太熟悉,想找一個動態的生成過程來參考,這是一個不錯的網站. 知識框架 圖的定義 線上性結構中,資料元素之間滿足唯一的線性關係,每個資料元素(除第一個和最後一個外)只有一個直接前趨和一個直接後繼; 在樹形結構中,資料元素之間有著明顯的層次關係,

資料結構——常用演算法實現(DFS,BFS,小生成樹,路徑,拓撲序列)

最近在複習資料結構的圖的部分,所以就把這一部分的演算法實現了一下,程式碼有註釋,都能看明白,粘在編譯器上就可以跑。如果有寫的不好的地方請在留言區說明。 import java.util.ArrayList; import java.util.List; i

的遍歷、小生成樹路徑

這一篇我們要總結的是圖(Graph),圖可能比我們之前學習的線性結構和樹形結構都要複雜,不過沒有關係,我們一點一點地來總結,那麼關於圖我想從以下幾點進行總結: 1,圖的定義? 2,圖相關的概念和術語? 3,圖的建立和遍歷? 4,最小生成樹和最短路徑? 5,演算法實現? 回到頂部一,圖的定義 什麼

相關(二)的鄰接矩陣表示(C++)路徑演算法

一.Dijikstra演算法 注意:計算最短路徑時,需要把鄰接矩陣中沒有邊的位置初始化為無窮大;此處以INF表示,INF可以取0x3f3f3f3f,不能直接設為INT_MAX(因為做加法時會上溢)。 測試用圖: 其鄰接矩陣表示為: vector<vector<int