最短路---Dijkstra學習筆記


最短路---Dijkstra

最近蒟蒻的自己重新學習了一遍最短路,也算有些體會,記錄下來。

首先引入問題:在一張圖中,從某一頂點出發,沿圖的邊到達定一個頂點所經過的路徑中,各邊權值和最小的一條路徑。

解決該問題的算法有:Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法

 

Dijkstra算法:

介紹:Dijkstra算法是單源最短路算法的一種,用於求出發節點到所有可達節點的最短路長度。

限制:路徑權值必須為非負數  無負權回路  單源最短路

算法思想:Dijkstra算法運用了貪心的思想,通過不斷尋找最短距離的點,用以該點為弧尾的點的邊更新其他的路徑(松弛)。設起點為start,終點為endstart-->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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM