Dijkstra求最短路徑(普通&堆優化)&例題


講了半天好像也許maybe聽懂了一點,先寫下來233

先整理整理怎么存(開始繞)

最簡單的是鄰接矩陣存,但是開到10000*10000就MLE了,所以我們用鏈式前向星存(據說是叫這個名字吧)

這是個什么鬼玩意呢?

我們在記錄時,以輸入的順序記錄。

我們記錄一條邊,就記下它的終點(to),權值(就是邊長)(dis),以及和這條邊的起點相同,編號稍微小一點的邊的編號(next)(開始繞)

這里我們記錄當前每個點的所有出邊(就是起點是這個點的邊)中編號最大的那一條邊(因為上面的next是編號稍微小的邊)

當然也可以依據自己的習慣存儲邊

先上段代碼

int head[nmax],n,m,s;//head[i] 是 以 點 i 為 起 點 , 所 有 出 邊 中 編 號 最 大 的 一 個
priority_queue<pair<int,int> > q;
void add(int fr,int _to,int _dis)
{    cnt++;
     eage[cnt].to=_to;
     eage[cnt].dis=_dis;
     eage[cnt].next=head[fr];//fr 為 from 的 簡 寫 , 這 里 的 以 點 i 為 起 點 的 邊 多 了 一 條,
 //所 以 上 一 個 以 點 i 為 起 點 的 編 號 最 大 的 邊 就 是 這 里 的 以 i 為 起 點 編 號 最 大 的 邊 的 上 一 條 邊 
     head[fr]=cnt; //更 新 head[i] 
}Edge [50001];
const int inf=2147483647;
int main()
{  scanf("%d%d%d",&n,&m,&o_node);
   dis[o_node]=0;
   for(int i=1;i<=m;i++)
   {int from,to,dis;
   cin>>from>>to>>dis;
     add(from,to,dis);
   }

這一坨是存圖

拿張圖舉個例子

假設我們輸入邊的數據如下(三個數n,m,s,n為起點,m為終點,s為邊長)

1 2 2

2 3 2

1 3 5

2 4 1

3 4 2

1 4 4

那代碼中的存儲如下

Edge[1].to=2,Edge[1].dis=2,Edge[1].next=0,head[1]=1(這里指沒有上一條邊),head[1]=1(這里head[i]記錄的是以i為起點,當前最大編號出邊的編號)

Edge[2].to=3,Edge[2].dis=2,Edge[2].next=0,head[2]=2

Edge[3].to=3,Edge[3].dis=5,Edge[3].next=1,head[1]=3

.....................................

講完存圖,再來說這個算法是怎么實現的

要求最短路徑,這里有點類似貪心。

首先選擇一個距離起點最近的直達點b,記錄當前點與b的距離,再由b進行相同的擴展,來更新起點與其它點的距離

這樣更新了一圈后就是最短距離,

再舉個栗子

 

沒錯還是剛才那張圖,這里標出了每條邊的權值

按照dijkstra算法,我們首先找到距離①最近的直達點②,由②更新出①到④的最短路為3,①到③的最短路為4,

那么程序怎么實現呢?

看注釋吧

(代碼from gh,注釋自己加的)

#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;
const int INF = 2147483647;
struct edge
{
    int to, dis_, next;
} Edge[500001];
struct node
{
    int to, dis;
    inline friend bool operator<(const node &a, const node &b)
    {
        return a.dis < b.dis;//構造函數,將優先隊列按照權值從小到大排序
    }
};
int head[500001], dis[10001];
bool vst[10001];
int nodenum, edgenum, origin_node, cnt = 1;
priority_queue<node> q;//優先隊列

inline void add_edge(int from, int to, int value)
{
    Edge[cnt].to = to;
    Edge[cnt].dis_ = value;
    Edge[cnt].next = head[from];
    head[from] = cnt++;
}

inline void dijkstra()
{
    for (register int i = 1; i < origin_node; i++)
    {
        dis[i] = INF;//全部初始化為一個很大的數
    }
    dis[origin_node]=0;
    for (register int i = origin_node + 1; i <= nodenum; i++)
    {
        dis[i] = INF;
    }
    q.push((node){origin_node, 0});
    while (!q.empty())//隊不空(這里是當廣搜來做的)
    {
        int x = q.top().to;
        q.pop();
        if (vst[x])//如果訪問過,就跳過
            continue;
        vst[x] = 1;
        for (register int i = head[x]; i; i = Edge[i].next)//從以x為起點的最后一條邊開始,一直遍歷完這個點的所有邊
        {
            dis[Edge[i].to] = min(dis[Edge[i].to], dis[x] + Edge[i].dis_);//比較原來的大小和以x點為中轉后的大小(取小的)
            q.push((node){Edge[i].to, -dis[Edge[i].to]});//入隊
        }
    }
}

template <typename T_>
inline T_ getnum()
{
    T_ res = 0;
    bool flag = false;
    char ch = getchar();
    while (!isdigit(ch))
    {
        flag = flag ? flag : ch == '-';
        ch = getchar();
    }
    while (isdigit(ch))
    {
        res = (res << 3) + (res << 1) + ch - '0';
        ch = getchar();
    }
    return flag?-res:res;
}
template<typename T_>
inline void putnum(T_ num)
{
    if (num<0)
    {
        putchar('-');
        num=-num;
    }
    if (num>9)putnum(num/10);
    putchar('0'+num%10);
}

int main()
{
    nodenum = getnum<int>(), dgenum  = getnum<int>(),origin_node = getnum<int>();
    for (register int i = 1; i <= edgenum; i++)
    {
        register int f, t, v;
        f = getnum<int>(), t = getnum<int>(), v = getnum<int>();
        add_edge(f, t, v);
    }
    dijkstra();
    for (register int i=1;i<=nodenum;putchar(' '),i++)
    {
        putnum<int>(dis[i]);
    }
    return 0;
}

順便附上一道dijkstra的題

一本通之城市路:

 這個好像就是個模板哈

(代碼from題解)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<set>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 3001
#define MOD 123
#define E 1e-6
using namespace std;
struct node{
    int pre;
    int next;
    int w;
}a[N*10];
int n,m;
int cnt;
int head[N],vis[N],f[N];
void add(int x,int y,int w)
{
    cnt++;
    a[cnt].pre=y;
    a[cnt].next=head[x];
    a[cnt].w=w;
    head[x]=cnt;
 
    cnt++;
    a[cnt].pre=x;
    a[cnt].next=head[y];
    a[cnt].w=w;
    head[y]=cnt;
}//存圖
 
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y,w;
        cin>>x>>y>>w;
        add(x,y,w);
    }
    
    memset(f,INF,sizeof(f));
    f[1]=0;
    vis[1]=1;
 
    int x=head[1];//手動模擬第一次出隊
    while(x!=0)
    {
        int y=a[x].pre;
        if(f[y]>a[x].w)
            f[y]=a[x].w;
        x=a[x].next;
    }
 
    int cnt=0;
    while(cnt<n)//遍歷所有的點
    {
        cnt++;
        int k;
        int minn=INF;
        for(int i=1;i<=n;i++)
            if(vis[i]==0&&f[i]<minn)
            {
                minn=f[i];
                k=i;
            }//先把能賦值的距離賦值上
        vis[k]=1;
 
        int x=head[k];//手動模擬for循環
        while(x!=0)//這里木有隊列,所以要while循環一次處理完
        {
            int y=a[x].pre;
            int w=a[x].w;
            if(vis[y]==0&&f[y]>f[k]+w)
                f[y]=f[k]+w;
            x=a[x].next;
        }
    }
    
    if(f[n]==INF)
        cout<<"-1"<<endl;
    else
        cout<<f[n]<<endl;
    return 0;
}

 堆優化

  我們上面說到dij是先挑距離起點最近的一個點b搞,然后再找距離b最近的點搞,那么每次判斷距離就有點麻煩。我們換成每次挑距離起點最近的點搞,這樣我們可以用堆(priority_queue)來維護距離起點最近的那個點,時間復雜度O(nmlogn)

代碼:

#include<bits/stdc++.h>
#define pa pair<int,int>
using namespace std;
inline int read()
{
    char ch=getchar();
    int x=0;bool f=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f?-x:x;
}
int n,m,dis[100009],cnt,head[100009],s;
struct Ed{
    int to,dis,nxt;
}edge[200009];
inline void add(int fr,int to,int dis)
{
    cnt++;
    edge[cnt].to=to;
    edge[cnt].dis=dis;
    edge[cnt].nxt=head[fr];
    head[fr]=cnt;
}
priority_queue<pa,vector<pa>,greater<pa> > q;//大根堆轉小根堆
bool vis[100009];
inline void dij(int s)
{
    for(int i=1;i<=n;i++)
     dis[i]=2147483645;
    dis[s]=0;
    q.push(make_pair(0,s));
    while(!q.empty())
    {
        int now=q.top().second;
        q.pop();
        if(vis[now])continue;
        vis[now]=1;
        for(int e=head[now];e;e=edge[e].nxt)
        {
            int v=edge[e].to;
            if(dis[now]+edge[e].dis<dis[v])
            {
                dis[v]=dis[now]+edge[e].dis;
                q.push(make_pair(dis[v],v));
            }
        }
    }
}
int main()
{
    n=read();m=read();s=read();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read(),w=read();
        add(u,v,w);
    }
    dij(s);
    for(int i=1;i<=n;i++)
     printf("%d ",dis[i]);
}

 


免責聲明!

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



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