算法初探 - 最短路徑

更新記錄

【1】2020.05.21-00:36ios

1.完善dijkstra
【2】2020.05.21-11:25
1.完善dijkstra堆優化算法

正文

鉛製芝士(會一點點就行啦~)

  • 動態規劃
  • 貪心
  • 鏈式前向星

持續更新中...

在學習圖論算法的時候,最短路算法就是必學算法之一
那麼既然它這麼重要,就更須要咱們深刻了解,熟練掌握
默認圖爲連通圖學習

dijkstra算法

有點貪心動規的意思優化

  • 咱們能夠發現,起點到任何一個點的最短路都要通過至少一個的中轉點(起點s也算一個中轉點)
  • 那麼咱們發現,若是想求出這個點的最短路,那麼一定要求出它中轉點的最短路
    • 首先dijkstra算法不能處理負邊權,因此咱們在使用時默認圖無負邊權
    • 因此1-其中一個中轉點的最短路,必定小於等於1-終點的最短路
    • 當小於時,中轉點比其先肯定最短路
    • 當等於時,誰先處理都是同樣的結果
  • 那麼依此類推,最後終將推到一個最最最簡單的問題:兩點間的最短路

這個問題只要你會存圖人就會作
(很像動態規劃對不對)spa


^RT,咱們要求出1-5的最短路徑

就必須求出起點到中轉點的最短路徑,中轉點爲4

想求出1-4的最短路徑,就先去求1-3的最短路徑

同理求1-2

而1-2的最短路就是其鏈接邊的權值3d

算法思路:code

定義變量(鏈式前向星的那堆變量就再也不重複寫了):
dis[i]:表示從起點到i的最短距離
f[i]:記錄這條邊有沒有被肯定過最短路
s表示起點blog

初始化:dis[i]=∞;dis[s]=0排序

遍歷每個點隊列

  • 對於每個點a,找到一個dis[b]最小的頂點b
  • b被肯定過最短路
  • 遍歷全部以b爲起點的邊,更新它們的dis
    算法結束
#include<iostream>
using namespace std;
#define NUM 500050
#define INF 2147483647
struct Edge{
	int na,np,w;
}e[NUM];
int head[NUM],dis[NUM],num,n,m,s,u,v,w,minn;
bool f[NUM];
inline void add(int f,int t,int w){
	e[++num].na=head[f];
	e[num].np=t,e[num].w=w;
	head[f]=num;
}
int main(){
	cin>>n>>m>>s;
	for(int i=0;i<m;i++){
		cin>>u>>v>>w;
		add(u,v,w);
	}
//初始化
	for(int i=1;i<=n;i++) dis[i]=INF;
	dis[s]=0;
//遍歷每個點
	for(int i=1;i<=n;i++){
//對於每個點a,找到一個dis[b]最小的頂點b
		minn=-1;
		for(int o=1;o<=n;o++)
			if(f[o]==0&&(dis[minn]>dis[o]||minn==-1)) minn=o;
//b被肯定過最短路
		f[minn]=1;
//遍歷全部以b爲起點的邊,更新它們的dis
		for(int o=head[minn];o!=0;o=e[o].na)
			if(!f[e[o].np])
				dis[e[o].np]=min(dis[e[o].np],dis[minn]+e[o].w);
	}
//算法結束,輸出s到各點的最短距離
	for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
}

dijkstra堆優化

咱們能夠發現,對於原來的dijkstra算法,每次查找最小值時間複雜度都爲O(n)

那麼有什麼算法能夠在常數時間內求出最小值呢?
固然是(線段樹)堆啦!

創建一個小根堆,便可迅速求出全部數據的最小值

那麼咱們發現,對於每次掃描,會有一些數據已經肯定過最小值,再次進行掃描會浪費時間
因此咱們要使用隊列來實現

最終結論:用優先隊列+二元組實現
明白了這個以後,這道題對你來講+岩漿=黑曜石

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
#define ll int
#define NUM 500050
#define INF 2147483647
struct Edge{
	int na,np,w;
}e[NUM];
ll head[NUM],dis[NUM],num,n,m,s,u,v,w,minn,bf,i;
bool f[NUM];
priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >q;
inline void add(int f,int t,int w){
	e[++num].na=head[f];
	e[num].np=t,e[num].w=w;
	head[f]=num;
}
inline int read() {
    int X=0,W=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') W=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=(X<<3)+(X<<1)+c-'0',c=getchar();
    return X*W;
}
int main(){
	n=read();m=read();s=read();
	for(i=0;i<m;++i){
		u=read();v=read();w=read();
		add(u,v,w);
	}
	for(int i=1;i<=n;++i) dis[i]=INF;
	dis[s]=0;
//以上所有爲初始化
	q.push(make_pair(0,s));
//將起點壓進隊列
	while(q.size()){
//若是隊列裏還有元素
		bf=q.top().second;q.pop();
//bf爲當前次遍歷的起點,保存後將這個元素彈出
		if(f[bf]) continue;
//搜過了就不搜了
		f[bf]=1;
//沒搜過就標記一下
		for(int i=head[bf];i;i=e[i].na){
//一樣的遍歷
			if(dis[bf]+e[i].w<dis[e[i].np]){
//找到了更短的路徑
				dis[e[i].np]=dis[bf]+e[i].w;
//更新
				q.push(make_pair(dis[e[i].np],e[i].np));
//既然有更優解那就將這個點壓進隊列,用來更新其餘點
			}
		}
	}
	for(int i=1;i<=n;++i) printf("%d ",dis[i]);
//out
}

那麼至於爲何make_pair的參數是最短距離,邊的終點呢?

爲何不反過來存或存其餘的參數呢

由於這是個自動排序的優先隊列
由於二元組自帶排序規則

相關文章
相關標籤/搜索