1. 程式人生 > 實用技巧 >ACwing(基礎)--- Dijkstra演算法(含堆優化版)

ACwing(基礎)--- Dijkstra演算法(含堆優化版)

樸素Dijkstra演算法

  • 時間複雜是 O(n^2+m), n 表示點數,m 表示邊數
  • 適合稠密圖
#include<cstring>
#include<iostream>
#include<algorithm>
#define mm(a,x) memset(a,x,sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;

const int maxn = 510;

int n,m;
int mp[maxn][maxn];
int dist[maxn];
int vis[maxn];

// 求1號點到n號點的最短路,如果不存在則返回-1
int dijkstra(){
    mm(dist,0x3f);//初始化距離  0x3f代表無限大
    dist[1]=0;//第一個點到自身的距離為0
    for(int i=0;i<n;i++){
        int t=-1; // 在還未確定最短路的點中,尋找距離最小的點
        for(int j=1;j<=n;j++)//從一號點開始
            if(!vis[j]&&(t==-1||dist[t]>dist[j]))
            t=j;
        vis[t]=1;
        // 用t更新其他點的距離
        for(int j=1;j<=n;j++)
            dist[j]=min(dist[j],dist[t]+mp[t][j]);
    }
    if(dist[n]==inf) return -1;//如果第n個點路徑為無窮大即不存在最低路徑
    return dist[n];
}

int main(){
    cin>>n>>m;
    mm(mp,0x3f);
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        mp[a][b]=min(mp[a][b],c);
    }
    int t=dijkstra();
    cout<<t;
    return 0;
}

堆優化版Dijkstra

  • 時間複雜度為O(mlogn),n表示點數,m表示邊數
  • 堆優化版的dijkstra是對樸素版dijkstra進行了優化
  • 在樸素版dijkstra中時間複雜度最高的尋找距離最短的點O(n^2)可以使用最小堆優化。
  1. 一號點的距離初始化為零,其他點初始化成無窮大。
  2. 將一號點放入堆中。
  3. 不斷迴圈,直到堆空。每一次迴圈中執行的操作為:
    彈出堆頂(與樸素版diijkstra找到S外距離最短的點相同,並標記該點的最短路徑已經確定)。
    用該點更新臨界點的距離,若更新成功就加入到堆中。
時間複雜度分析

尋找路徑最短的點:O(n)

加入集合S:O(n)

更新距離:O(mlogn)

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#define mm(a,x) memset(a,x,sizeof a)
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int ,int > PII;
const int maxn = 1e6+10;

int n,m;
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
int dist[maxn],vis[maxn];

void add(int a,int b,int c) {
	e[idx] =b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int dijkstra() {
	mm(dist,inf);
	dist[1]=0;
	priority_queue<PII,vector<PII>,greater<PII>> heap; // 定義一個小根堆
	heap.push({0,1});// 這個順序不能倒,pair排序時是先根據first,再根據second,這裡顯然要根據距離排序
	while(heap.size()) {
		auto t =heap.top();// 取不在集合S中距離最短的點
		heap.pop();
		int ver=t.second,distance = t.first;
		if(vis[ver]) continue;
		vis[ver]=1;//標記該點
		for(int i = h[ver];i!=-1;i = ne[i]){
			int j = e[i];// i只是個下標,e中在存的是i這個下標對應的點
			if(dist[j] > distance + w[i]){
				dist[j] = distance + w[i];
				heap.push({dist[j],j});//放入堆中,更新其他點
			}
		}
	}
	
	if(dist[n] == inf) return -1;
	return dist[n];
}

int main() {
	scanf("%d%d",&n,&m);
	mm(h,-1);
	while(m--){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
	}
	int t=dijkstra();
	printf("%d\n",t);
	return 0;
}