最短路---Dijkstra
最近蒟蒻的自己重新學習了一遍最短路,也算有些體會,記錄下來。
首先引入問題:在一張圖中,從某一頂點出發,沿圖的邊到達定一個頂點所經過的路徑中,各邊權值和最小的一條路徑。
解決該問題的算法有:Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法
Dijkstra算法:
介紹:Dijkstra算法是單源最短路算法的一種,用於求出發節點到所有可達節點的最短路長度。
限制:路徑權值必須為非負數 無負權回路 單源最短路
算法思想:Dijkstra算法運用了貪心的思想,通過不斷尋找最短距離的點,用以該點為弧尾的點的邊更新其他的路徑(松弛)。設起點為start,終點為end,start-->end的最短路徑只有兩種情況。
1、start--->end (從起點到終點直接為最短路徑)
2、Start--->v1--->v2...--->end (從起點通過其他的點到達終點的路徑)
算法步驟:
把頂點V分成兩組:
S:已經求出最短路徑的頂點集合
T=V-S:尚未確定最短路徑的頂點集合
1、初始時:令S={V0} T={其余頂點} T中的頂點對應的距離值若存在<V0,Vi>,則為該邊的權值,若不存在則為INF(正無窮)
2、從T中選取一個距離最小的頂點W,將該點加入集合S中。並用該點對T中頂點的距離進行修改:若加入w作為中間頂點(V0-->W-->Vn),該路徑的距離比不加入W的路徑更短,則修改此距離值。
3、重復上述步驟,知道S中包含所有頂點,即S=V為止
算法實現過程中需要兩個數組
1、dist數組記錄源點到每個點的最短路徑大小
2、vis數組記錄該點是否已經在集合S中(即是否已經找到到該點的最短路徑)
算法過程(手動模擬,QAQ丑字不要介意)
代碼實現
這里用一道最短路的模板題來展示該算法的代碼。(POJ - 2387 Til the Cows Come Home)
題意:給你n個點,給出a到b的距離,a,b邊是可以互想抵達的,求1到n的最短距離。實質:n個頂點m條邊的無向圖,求1到n的最短路徑。
//鄰接矩陣存圖版本 #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int MAX=1005; const int INF=0x3f3f3f3f; int t,n; //邊數 點數 int map[MAX][MAX]; //鄰接矩陣存圖 int dist[MAX]; //dist數組存源點到各個點的最短路徑 bool vis[MAX]; //標記是否找到最小值(是否在集合S中) void Dij(int start) { for(int i=0;i<=n;i++) //初始化 dist全為正無窮,vis全為false { dist[i]=INF; vis[i]=false; } dist[start]=0; //源點到源點的值為0 int min=INF,pos; //尋找dist數組中最小值,並記錄其所在的點 for(int i=1;i<=n;i++) { min=INF; for(int j=1;j<=n;j++) //尋找dist中的最小值 { if(!vis[j]&&dist[j]<min) { min=dist[j]; pos=j; } } vis[pos]=true; //將該點加入集合S中 for(int j=1;j<=n;j++) //用該點為弧尾的邊進行松弛操作 { if(!vis[j]&&dist[j]>dist[pos]+map[pos][j]) dist[j]=dist[pos]+map[pos][j]; } } } int main() { while(scanf("%d%d",&t,&n)!=EOF) { for(int i=1;i<=n;i++) //初始化 for(int j=1;j<=n;j++) map[i][j]=INF; for(int j=0;j<t;j++) { int a,b,len; scanf("%d%d%d",&a,&b,&len); if(map[a][b]>len) //去除重邊(如果先輸入的1 2 10則為點1-->2的距離為10,若在輸入1 2 100則為 map[a][b]=map[b][a]=len; //點1-->2的距離為100 但是第二次輸入會覆蓋第一次輸入,該更短的邊不見,影響結果) } Dij(1); //以1為源點進行Dijkstra printf("%d\n",dist[n]); } return 0; }
//鄰接表存圖 #include<iostream> #include<cstdio> #include<vector> #include<cstring> using namespace std; const int MAX=1005; const int INF=0x3f3f3f3f; int t,n,a,b,len; int dist[MAX]; bool vis[MAX]; struct point { int to,val; }; vector<point>e[MAX]; void Dij(int start) { for(int i=1;i<=n;i++) //初始化 { dist[i]=INF; vis[i]=false; } dist[start]=0; //源點到源點距離為0 int min,pos; for(int i=1;i<=n;i++) { min=INF; for(int j=1;j<=n;j++) //找dist數組的最小值 { if(!vis[j]&&dist[j]<min) { min=dist[j]; pos=j; } } vis[pos]=true; for(int j=0;j<e[pos].size();j++) //用找到的點為弧尾的邊進行松弛 { int to=e[pos][j].to,val=e[pos][j].val; if(!vis[to]&&dist[to]>dist[pos]+val) dist[to]=dist[pos]+val; } } } int main() { while(scanf("%d%d",&t,&n)!=EOF) { point temp; for(int i=0;i<t;i++) { scanf("%d%d%d",&a,&b,&len); //鄰接矩陣存圖 temp.to=a,temp.val=len; e[b].push_back(temp); temp.to=b;temp.val=len; e[a].push_back(temp); } Dij(1); printf("%d\n",dist[n]); } return 0; }
//鏈式前向星存圖 #include<iostream> #include<cstring> #include<cstdio> using namespace std; const int MAXN=4009; const int MAX=1009; const int INF=0x3f3f3f3f; int head[MAX],cnt=0; int t,n,a,b,len; int dist[MAX]; bool vis[MAX]; struct Edge{ int next,to,val; }Edge[MAXN]; inline void add(int u,int v,int w) { Edge[cnt].to=v; Edge[cnt].val=w; Edge[cnt].next=head[u]; head[u]=cnt++; } void Dij(int start) { for(int i=0;i<=n;i++) { dist[i]=INF; vis[i]=false; } dist[start]=0; int min,pos; for(int i=1;i<=n;i++) { min=INF; for(int j=1;j<=n;j++) { if(!vis[j]&&dist[j]<min) { min=dist[j]; pos=j; } } vis[pos]=true; for(int i=head[pos];i!=-1;i=Edge[i].next) { int to=Edge[i].to; if(!vis[to]&&dist[to]>dist[pos]+Edge[i].val) dist[to]=dist[pos]+Edge[i].val; } } } int main() { while(scanf("%d%d",&t,&n)!=EOF) { memset(head,-1,sizeof(head)); for(int i=0;i<t;i++) { scanf("%d%d%d",&a,&b,&len); add(a,b,len); add(b,a,len); } Dij(1); printf("%d\n",dist[n]); } return 0; }
如有錯誤和不足之處歡迎指點,謝謝大家~
參考:
http://www.cnblogs.com/hxsyl/p/3270401.html
https://blog.csdn.net/qq_35644234/article/details/60870719
https://blog.csdn.net/u012469987/article/details/51319574#dijkstra