Dijkstra


最短路徑

 

dijkstra

不可解決有負權的圖

但是若是沒有負權的話,最好用這個,而不要用spfa,因為spfa的時間復雜度極其不穩定

對於稀疏圖來說,spfa的時間復雜度確實是要比dijkstra要低

但,若是稠密圖的話,spfa的時間復雜度就極其不可觀了

所以,dijkstra的重要性顯然

 

1.定義

    不可解決有負權邊的圖

    時間復雜度為O(n2)

    經過堆優化的dijkstra的時間復雜度為O(nlogn)

 

2.算法描述

    貪心的思想(這是因為這個思想,所以判不出來負權邊--(咳咳,個人之見,逃~)

    以起始點為中心向外層層擴展(廣搜的思想),直到擴展到終點為止

基本思想:

    引入兩個集合S和U。

    S:記錄已求出最短路徑的頂點

    U:記錄未求出最短路徑的頂點

    (能到源點的所有點)

操作:

    1.初始時,S集合中只包含源點s

         U集合中包含除s以外的其他頂點 且 U中頂點的距離為“起點s到該頂點的距離”

         (若相連,即為邊權值;若不相連,則設其為無窮大)

    2.從U中選出“距離最短的頂點k”,將k加入s中,並將k從U集合中移出

    3.用新加入的頂點k來更新U集合中各個頂點到起點s的距離

     之所以更新U中頂點的距離,是因為 上一步中確定了k是求出最短路徑的頂點,從而可以用k來更新其他的值

    4.重復2.3,直到遍歷完所有點

 

 

(假裝已經會了)

 

普普通通的dijkstra

 

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

int map[110][110];//這就是map數組,存儲圖
int dis[10010];//dis數組,存儲估計值
int book[10010];//book[i]代表這個點有沒有被當做源點去搜索過,1為有,0為沒有。這樣就不會重復搜索了。
int n,m;

void dijkstra(int u)//主函數,參數是源點編號
{
    memset(dis,88,sizeof(dis));//把dis數組附最大值(88不是十進制的88,其實很大)
    int start=u;//先從源點搜索
    book[start]=1;//標記源點已經搜索過
    for(int i=1; i<=n; i++)
    {
        dis[i]=min(dis[i],map[start][i]);//先更新一遍
    }
    for(int i=1; i<=n-1; i++)//源點不用 
    {
        int minn=9999999;//這里的minn不是題解上的minn,這代表的是最近點到源點的距離,start才代表最近的點、
        for(int j=1; j<=n; j++)
            if(book[j]==0 && minn>dis[j])
            {
                minn=dis[j];
                start=j;//找到離源點最近的點,然后把編號記錄下來,用於搜索。
            }
        book[start]=1;
        for(int j=1; j<=n; j++)
            dis[j]=min(dis[j],dis[start]+map[start][j]);//以新的點來更新dis。
    }
}

int main()
{
    cin>>n>>m;
    memset(map,88,sizeof(map)); 
    for(int i=1; i<=m; i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        map[a][b]=c;
    }//有向圖吧 
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if(i==j)
                map[i][j]=0;//僅是初始化 
    dijkstra(1);//以1為源點。
    for(int i=1; i<=n; i++)
        cout<<dis[i]<<" ";
}

 (普普通通的dij也幾乎過不了什么,還得加上一個鏈式前向星)

 

堆優化的dijkstra

 

1.基本思想

 

//堆優化dijkstra 邊權不可為負 
#include<cstdio> #include<algorithm> #include<queue> #include<cstring>
using namespace std; inline int read()//快讀 
{ int sum = 0,p = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '0') p = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (sum *= 10) += ch - '0'; ch = getchar(); } return sum * p; } const int maxn = 1e5 + 5; const int maxm = 2e5 + 5; int n,m,s;//n為點的個數,m為變得個數,s為源點 
int tot,dis[maxn],wei[maxm];//dis為i點到源點的距離,wei為邊權 
int head[maxn],to[maxm],nxt[maxm];//鏈式前向星 
bool mrk[maxn];//標記該點是否被加入到最短路徑的生成樹中(0-沒有,1-有) 
priority_queue< pair<int,int> > q; /* 優先隊列:默認是大根堆 pair第一維為dis[i]的相反數(這樣就可以變成小根堆啦) 第二維為節點編號 */

void dijkstra() { memset(dis,0x3f,sizeof(dis)); dis[s] = 0;//dis初始化 起點為0,其余為正無窮
    q.push(make_pair(0,s)); while(q.size()) { int x = q.top().second; q.pop();//取堆頂
        if(mrk[x]) continue; mrk[x] = true; for(int i = head[x];i;i = nxt[i]) {//掃描所有出邊
            int y = to[i]; int z = wei[i]; if(dis[y] > dis[x] + z) { dis[y] = dis[x] + z; q.push(make_pair(-dis[y],y)); } } } } int main() { n = read(),m = read(),s = read(); int x,y,z; for(int i = 1;i <= m;i++) { x = read(),y = read(),z = read(); wei[++tot] = z; nxt[tot] = head[x]; to[tot] = y; head[x] = tot; }//構建鄰接矩陣 (通常可用add函數) 
 dijkstra(); for(int i = 1;i <= n;i++) printf("%d ",dis[i]); return 0; }

 

 

 

(我應該會回來補一補的qwq...)

 

 

 

    

    

    

 


免責聲明!

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



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