1. 程式人生 > >演算法之最短路

演算法之最短路

最短路

我跟你講SPFA已經死了好吧,SPFA+堆優又太難打,那就用dijkstra吧。(負權?我管它呢)

不加任何優化的裸dijkstra

一般般快吧,N^2,N=10000時可以卡過,很好打。

#include <bits/stdc++.h>
#define MAXN 1005
using namespace std;
long long LIS[MAXN][MAXN];//LIS陣列儲存圖
long long dis[MAXN];//dis陣列,儲存最短長度值 
long long vis[MAXN];//vis[i]代表這個點有沒有被當做源點去搜索過,1為有,0為沒有。這樣就不會重複搜尋了。
long long N,M,S;
void dijkstra(long long x)//主函式,引數是源點編號
{
    long long start=x;//先從源點搜尋  
	for(long long i=1;i<=N;i++) dis[i]=LIS[x][i];//先更新一遍
    dis[start]=0; 
	vis[start]=1;//標記源點已經搜尋過
    for(long long i=1;i<=N-1;i++)
    {
        long long MINN=2147483647;//比較
        for(long long j=1;j<=N;j++) if(vis[j]==0&&MINN>dis[j]) MINN=dis[j],start=j;//找到離源點最近的點,然後把編號記錄下來,用於搜尋,可優化
        vis[start]=1;        
        for(long long j=1;j<=N;j++) dis[j]=min(dis[j],dis[start]+LIS[start][j]);//以新的點來更新dis。
    }
}
int main()
{
    cin>>N>>M>>S;//N是點數,M是邊數,S是出發點 
    for (long long i=1;i<=N;i++)for (long long j=1;j<=N;j++)LIS[i][j]=2147483647;
    for (int i=1;i<=N;i++) LIS[i][i]=0;
    for(long long i=1;i<=M;i++)
    {
        long long a,b,c;
        cin>>a>>b>>c;
        LIS[a][b]=min(LIS[a][b],c);//判重 
        //LIS[b][a]=min(LIS[a][b],c);//這是雙向邊 
    }
    dijkstra(S);//以S為源點。
    for(long long i=1;i<=N;i++) cout<<dis[i]<<" ";
	return 0;
}

堆優化+前向星儲存dijkstra

因為普通dijkstra需要找最小值然後更新,所以對於

for(long long j=1;j<=N;j++) if(vis[j]==0&&MINN>dis[j]) MINN=dis[j],start=j;

我們可以考慮用堆優化此過程,並且用鏈來儲存,極大優化了記憶體

#include<bits/stdc++.h>
#define MAXN 100010
#define MAXM 200010
using namespace std;
long long last[MAXN],to[MAXM],nex[MAXM],v[MAXM],cnt=0,dis[MAXN];
bool vis[MAXN];  
long long n,m,s;
void add(int x,int y,int w)
{
    nex[++cnt]=last[x];//上一條邊的編號 
    to[cnt]=y;//此邊到哪個點 
    v[cnt]=w;//權值 
    last[x]=cnt;//以x開頭的最後一條邊的編號 
}
priority_queue< pair<int,int> > q;
void dijkstra(int s)
{
	for(int i=1;i<=n;i++) dis[i]=2147483647;//其實這樣也可以,因為會自己更新的 
    dis[s]=0;
    q.push(make_pair(0,s));//放入初始節點 
    while(!q.empty())//還未遍歷完 
	{
        int x=q.top().second;//堆頭節點,最小節點編號 
		q.pop();//用過彈出 
        if(vis[x]) continue;//已拓展的就不要再做了 
        vis[x]=1;
        for(int i=last[x];i;i=nex[i])//拓展 
		{
            int y=to[i];//下一個點 
            if(dis[y]>dis[x]+v[i])
			{
                dis[y]=dis[x]+v[i];
                q.push(make_pair(-dis[y],y));//用相反數實現小根堆,放入堆 
            }
        }
    }
}
int main()
{
    cin>>n>>m>>s;
    for(int i=1;i<=m;i++)
    {
		int x,y,z;
		cin>>x>>y>>z;
    	add(x,y,z);//單向邊 
	}
	dijkstra(s);
    for (int i=1;i<=n;i++) cout<<dis[i]<<" "; 
    return 0;
}