1. 程式人生 > 實用技巧 >單源最短路

單源最短路

dijkastra寫法

鄰接矩陣


#include <bits/stdc++.h>
using namespace std;
#define int long long
#define r read()
const int maxn = 1e4;
const int INF = 0x3f3f3f3f;
int n,m,q;
int G[maxn][maxn];
bool vis[maxn];/// 標記位
int close[maxn];///最近距離所在的終點
int dis[maxn];///最近的距離
void out(int x)
{   
    if(x==q){cout<<x<<' ';return ;}
    out(close[x]);
    cout<<x<<' ';
}
signed  main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	memset(G,INF,sizeof(G));///  按照邏輯含義  不存在的邊的長度為無窮大  但是邊相同的會被覆蓋
	memset(vis,false,sizeof (vis));
        memset(close,-1,sizeof(close));
	///freopen("1.txt","r",stdin);
	///freopen("2.txt","w",stdout);
	cin>>m>>n>>q;///n條邊  m個頂點
	for (int i=0;i<n;++i)
	{
		int u;
		int v;
		int w;
		cin>>u>>v>>w;
		G[u][v] = w;
	}
	for (int i=1;i<=m;++i)
	{
		dis[i] = G[q][i];///  首次更新  確立v1為永久點
		if (!vis[i] && dis[i]!=INF) close[i] = q;///  和當前永久點相連最近
	}
	int idx = q;
	vis[q] = true;
        dis[q] = 0;
	for (int i=1;i<m;++i)
	{
		int mine = INF;
		for (int k=1;k<=m;++k)
		{
			if(!vis[k]&&dis[k]<mine)
			{
				mine = dis[k];
				idx = k;
			}
		}   /// 找出當前的最小臨時點  設為永久點
		vis[idx] = true;///   設為永久點  進行標記
		/// cout<<close[idx]<<' '<<idx<<endl;
		for (int j=1;j<=m;++j)
		{
			if (!vis[j]&&dis[idx]+G[idx][j] < dis[j])/// 更新永久點相連的頂點
			{
				close[j] = idx;
				//sum -= dis[j];
				dis[j] = dis[idx]+G[idx][j];
				//sum += dis[j];
			}
		}
      }
      /*for (int i=1;i<=m;++i)
      {
         cout<<close[i]<<' ';
      }*/
      ///puts("");
      out(m);
      return 0;
}

/*
    dijkstra演算法    本種寫法針對於無重邊的情況  ,主要採用的思想是貪心
    每次利用上一次得到的永久點進行更新
    首先將起始點設為永久點  然後對於和永久點相連的邊進行更新   更新最短的距離dis[i]
    然後對於剩餘的臨時點查詢其中的最小值設為新的永久點  用當前的永久點進行更新
    如此往復  每次更新到當前最近的距離的頂點   對於記錄vq到所有點的最短路徑,進行回溯  
*/

鄰接表寫法

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define r read()
const int maxn = 1e4;
const int INF = 0x3f3f3f3f;
int n,m,q;
bool vis[maxn];
int dis[maxn];///   儲存從開始位置噹噹前下標節點的最短距離
int close[maxn];  ///儲存最短距離最近的頂點編號
struct vex_edge ///    頭節點結構
{
    struct edge_Node *first_Node;///   儲存第一個邊節點
}G[maxn];
struct edge_Node
{
    int idx;///   跟G[i]-idx   表示 從i到idx的邊
    int weight;/// 權重
    struct edge_Node * next_edge;///   下一個邊節點  
};
void out(int x)
{   
    if(x==q){cout<<x<<' ';return ;}
    out(close[x]);
    cout<<x<<' ';/// 逆序輸出
}
void   init()/// 初始化
{
    memset(vis,false,sizeof(vis));
    memset(close,-1,sizeof(close));
    memset(dis,INF,sizeof(dis));
    for (int i=1;i<=m;++i)   G[i].first_Node = NULL;///  頭節點首先為空   此時是不存在的頭節點   只是一個指標
    /// 有需要也可以自行弄一個頭節點
}
signed main()
{
    cin>>m>>n>>q;/// n條邊   m個節點  q是開始出發的起點
    init();/// 初始化
    for (int i=1;i<=n;++i)
    {
        int u,v,w;
        cin>>u>>v>>w;
        struct edge_Node*p;
        p = (edge_Node*)malloc(sizeof(edge_Node));
        p->idx = v;
        p->weight = w;
        p-> next_edge = G[u].first_Node;  ///  插入操作  將所有的鏈節點放到p之後
        G[u].first_Node = p;
    }
    /*
    for (int i=1;i<=m;++i)
    {
        struct edge_Node *op;
        op =  G[i].first_Node;
        cout<<i<<' ';
        while (op!=NULL)
        {
            cout<<op->idx<<' '<<op->weight<<' ';
            op = op ->next_edge; G[q].first_Node;
        }
        cout<<endl;
    }
    */
    struct edge_Node *op;
    op = G[q].first_Node;
    while (op != NULL)  /// 找出跟q相連的邊節點
    {
        dis[op->idx] = op->weight;
        close[op->idx] = q;
        ///cout<<op->weight<<endl;
        op = op ->next_edge;
    }
	int id = q;
	vis[q] = true;
    dis[q] = 0;  ///
    close[q] = q;/// 最後回溯路徑的基礎條件
	for (int i=1;i<m;++i)
	{  
		int mine = INF;
		for (int k=1;k<=m;++k)
		{
			if(!vis[k]&&dis[k]<mine)
			{
				mine = dis[k];
				id = k;
			}
		}   /// 找出當前的最小臨時點  設為永久點
		vis[id] = true;///   設為永久點  進行標記
        ///cout<<id<<endl;
		/// cout<<close[idx]<<' '<<idx<<endl;
        struct edge_Node *T;
        T = G[id].first_Node;
        while (T!=NULL)
        {
            if (!vis[T->idx]&&dis[T->idx] > (dis[id]+(T->weight)))
            {
                close[T->idx] = id;
                dis[T->idx] = dis[id] +T->weight;
               /// cout<<T->idx<<' '<<dis[T->idx]<<endl;
            }
            T = T->next_edge;
        }
    }
    for (int i=1;i<=m;++i)
       cout<<close[i]<<' ';
   puts("");
   for (int j=1;j<=m;++j) out(j),puts("");/// 輸出每一條最短路徑
   return 0;
}