Dijkstra算法,你想要的全都有!!!

前言

王國維《人間詞話》中曾提出,古今之成大事業者,須經過三重境界。這三重境界體現的正是儒家精神,所以正是路徑所在。
  第一重境界是“昨夜西風凋碧樹,獨上高樓,望盡天涯路”。登上高樓,遠眺天際,正是躊躇滿志,志存高遠,高瞻遠矚,一腔抱負。人生,志向決定方向,格局決定高度;小溪只能入湖,大河則能入海。所以做事,要先立心中志向;成事,要先拓胸中格局。
  第二重境界是“衣帶漸寬終不悔,爲伊消得人憔悴”。事情是需要去做才能成的,成越大的事業,需要越大的努力和付出,甚至要經受越大的磨難和困苦。這個世間,從來都是“艱難困苦,玉汝於成”;所以無論如何,都要“天行健,君子以自強不息”。
  第三重境界是“衆裏尋她千百度,驀然回首,那人卻在,燈火闌珊處”。這說的是歷經磨難而逐漸成熟、成長,最終豁然貫通、水到渠成。這其中蘊含一個重要道理,就是蘇東坡所說的“厚積而薄發”。只有厚積才能薄發,人要做的,就是不斷厚積,等待薄發。這就是拿得起的完整路徑,也是事業成功的完整過程。
  剛好Dijkstra算法也可以借用三重境界。

思想

Dijkstra算法採用的是一種貪心的策略,聲明一個數組dis來保存源點到各個頂點的最短距離和一個保存已經找到了最短路徑的頂點的集合:T
1.初始時,原點 s 的路徑權重被賦爲 0 (dis[s] = 0)。若對於頂點 s 存在能直接到達的邊(s,m),則把dis[m]設爲w(s, m),同時把所有其他(s不能直接到達的)頂點的路徑長度設爲無窮大。初始時,集合T只有頂點s。
2.從dis數組選擇最小值,則該值就是源點s到該值對應的頂點的最短路徑,並且把該點加入到T中,此時完成一個頂點,
3.繼續從dis數組選擇最小值,我們需要看看新加入的頂點是否可以到達其他頂點並且看看通過該頂點到達其他點的路徑長度是否比源點直接到達短,如果是,那麼就替換這些頂點在dis中的值。
4.重複上述動作,直到T中包含了圖的所有頂點。

算法演示

在這裏插入圖片描述
這張圖就是一個Dijkstra算法的過程,1爲根節點start
1.此時集合內只有1節點 最短距離爲1~2的距離7,把2加入集合,更新1-2距離
2.此時集合內有1,2節點,最短距離爲1~3的距離9,把3加入集合,更新1-3距離
3.此時集合內有1,2,3節點,最短距離爲3~6的距離2,把6加入集合,更新1-6距離
4.此時集合內有1,2,3,6節點,最短距離爲3~4的距離11,把4加入集合,更新1-4距離
5.最後,最短距離爲6~5的距離9,更新1-5距離,結束

三個代碼模板

步入正軌,既然說了有三重境界,那肯定有三個代碼。

代碼1,實現並打印出路徑

打印路徑這裏,新建一個數組p記錄路徑整合的節點,利用棧的性質打印出路徑

#include <bits/stdc++.h>
using namespace std;


const int MAX_CITY_NUM = 100;//節點數目的最大值
const int MAX_POLICY = 1e7;//節點權值的最大值
int mp[MAX_CITY_NUM][MAX_CITY_NUM];
int dist[MAX_CITY_NUM];//源點到其他節點的距離 
int p[MAX_CITY_NUM];//記錄路徑 
int n, m;
bool flag[MAX_CITY_NUM];

void  dijkstra(int start)
{
	for (int i = 1; i <= n; ++i)//初始化 
	{
		flag[i] = false;
		dist[i] = mp[start][i];
		if (dist[i] != MAX_POLICY)
		{
			p[i] = start;
		}
		else
		{
			p[i] = -1;
		}
	}	
	flag[start] = true;
	dist[start] = 0;
	for (int i = 1; i <= n; ++i)
	{
		int min_dist = MAX_POLICY, t = start;
		for (int j = 1; j <= n; j++) //找到最小權值 
		{
			if (dist[j] < min_dist && !flag[j]) 
			{
				min_dist = dist[j];
				t = j;
			}
		}
		if (t == start) return;//標記退出循環 
		flag[t] = true;
    	for (int j = 1; j <= n; j++)//記錄最小邊 
		{
			if (mp[t][j] < MAX_POLICY && !flag[j])
			{
				if (dist[j] > (dist[t] + mp[t][j]))
				{
					dist[j] = dist[t] + mp[t][j];
					p[j] = t;
				}
			}
		}
	}
	
}

void showProcess(int start)//利用棧的性質  輸出路徑 
{
	int value;
	stack<int> stack;
	for (int i = 1; i <= n; ++i)
	{
		value = p[i];
		cout << start << "到"<< i << "的路徑是"; 
		while (value != -1)
		{
			stack.push(value);
			value = p[value];
		}
		while (!stack.empty())
		{
			int node = stack.top();
			stack.pop();
			cout << node << "-";
		}
		if(dist[i]==MAX_POLICY)
		{
			cout<< i <<" "<< "最短距離爲" << "無窮大" <<endl;
		}
		else cout<< i <<" "<< "最短距離爲" << dist[i] <<endl;
	}
}
int main()
{
	int u, v, w, start;
	cin >> n;
	cin >> m;
	for (int i = 1; i <= n; ++i) 
	{
		for (int j = 1; j <= n; ++j)
		{
			mp[i][j] = MAX_POLICY;	
		}
	}
	for (int i = 0; i < m; ++i) 
	{
		cin >> u >> v >> w;
		mp[u][v] = min(mp[u][v], w);
	}
	cin >> start;
	dijkstra(start);
	showProcess(start);
	return 0;
}
代碼2 堆優化鄰接矩陣存圖

想優化時間效率,別急,既然我們每次都是找與集合最小的,那麼就出現了優先級,可以使用優先隊列來優化

#include<bits/stdc++.h>
using namespace std;

#define INF  0x3f3f3f3f   //定義一個很大的數
  
struct Node
{
	int num,val;   //存放結點編號和到初始點的距離 
}nod;

priority_queue<Node> qq;;   //優先從小到大
  
bool operator < (Node a,Node b)
{
	if(a.val == b.val) return a.num>b.num;
	return a.val>b.val;              //先出小 
}

int book[100];  //檢查這個點是否用過 
int dis[100];     //到原點最短距離 
int D[100][100];  //記錄路徑長度
int V,E;
int start;

int main()
{
	int a,b,d;
	cin>>V>>E;  //輸入頂點數和邊數 
	//while(!qq.empty()) qq.pop(); //清空
	memset(book,0,sizeof(book));
	memset(D,-1,sizeof(D)); 
	for(int i=0;i<E;i++)
	{
		cin>>a>>b>>d;
		D[a][b] = d;
	}
 	cin>>start;
	for(int i=1;i<=V;i++)
	dis[i]=INF;
	dis[start]=0;
	nod.num=start;
	nod.val=0;
	qq.push(nod);   //將起點放入隊列 
 
	while(!qq.empty())  //不爲空時 
	{
		for(int i=1;i<=V;i++)
		{
			if(i==start) continue;
			if(D[qq.top().num][i]!=-1&&dis[i]>dis[qq.top().num] + D[qq.top().num][i]) 
			{
				dis[i]=dis[qq.top().num] + D[qq.top().num][i];
				nod.num=i; nod.val=dis[i];
				qq.push(nod);
			}
		}
		qq.pop();
	}
	for(int i=1;i<=V;i++)
	{
		cout<<dis[i]<<" ";
	}
	return 0;
}
代碼3 堆優化鄰接表存圖 並輸出路徑數量

終極版,模板就用他了,當二維數組過小runtime不能有效存圖時,鄰接表登場,因爲最短路徑可能不止一條,如果考到,不好意思,萬事俱備,還可以根據有向圖無向圖進行修改,完美模板

#include<bits/stdc++.h>
using namespace std;

const int MaxN = 100010, MaxM = 500010;

struct edge
{
    int to, dis, next;
};

edge e[MaxM];
int head[MaxN], dis[MaxN], cnt=0;
bool vis[MaxN];
int n, m, s;
int ans[MaxN];//記錄數量 
int mod=100003;

inline void add_edge( int u, int v, int d )
{
    cnt++;
    e[cnt].dis = d;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}

struct node
{
    int dis;//h
    int pos;//vis
    bool operator <( const node &x )const
    {
        return x.dis < dis;
    }
};

priority_queue<node> q;

inline void dijkstra(int s)
{
    dis[s] = 0;
    q.push( ( node ){0, s} );
    while( !q.empty() )
    {
        node tmp = q.top();
        q.pop();
        int x = tmp.pos, d = tmp.dis;
        if( vis[x] )
            continue;
        vis[x] = 1;
        for( int i = head[x]; i; i = e[i].next )
        {
            int y = e[i].to;
            if( dis[y] > dis[x] + e[i].dis )
            {
                dis[y] = dis[x] + e[i].dis;
				ans[y]=ans[x];
                if( !vis[y] )
                {
                    q.push( ( node ){dis[y], y} );
                }
            }
            else if(dis[y]==dis[x]+e[i].dis)
            {
                ans[y]+=ans[x];
                ans[y]%=mod;
            }
        }
    }
}

int main()
{
    cin>>n>>m;cin>>s;
	ans[s]=1;
    for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;
    for(int i = 0; i < m; ++i)
    {
        int u, v, d;
        cin>>u>>v;//d=1;
        cin>>d;
        add_edge( u, v, d );
        //add_edge( v, u, d );
    }
    dijkstra(s);
    for( int i = 1; i <= n; i++ )cout<<dis[i]<<" ";
    //for( int i = 1; i <= n; i++ )cout<<ans[i]<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章