单源最短路

 一. 所有边权都是正数 (带记录答案路径)

(1)dijkstra(朴素版)  o ( n ^ 2 )   

· 稠密图(边数多) m 和 n ^ 2 是一个级别

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

struct node
{
	int v, w;
};

const int N = 5e4 + 5, INF = 0x3f3f3f3f;
int n, m, s, ans, d[N], vis[N];
vector<node> G[N];

void dijkstra(int s)
{
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	for (int i = 1; i <= n; i ++)
	{
		int u = 0;
		for (int j = 1; j <= n; j ++)
			if (d[j] < d[u] && !vis[j])					// 标记的作用是判断当前节点是否已经更新过其邻点 
				u = j;
		vis[u] = 1;										// 每个点只可能去更新一次其邻点 
		for (int j = 0; j < G[u].size(); j ++)
		{
			int v = G[u][j].v , w = G[u][j].w;
			d[v] = min(d[v], d[u] + w);
		}
	}
}

int main()
{
	cin >> n >> m >> s;
	for (int i = 1; i <= m; i ++)
	{
		int u, v, w;
		cin >> u >> v >> w;
		G[u].push_back({v, w});
	}
	dijkstra(s);
 	return 0;
}

(2)dijkstra(堆优化版) o ( m log n )

· 稀疏图  m 和 n 一个级别

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

struct node
{
	int v, w;
};

const int N = 5e4 + 5, INF = 0x3f3f3f3f;
int n, m, s, ans, d[N], vis[N], pre[N];
vector<node> G[N];
priority_queue<pair<int, int> > pq;

void dijkstra(int s)
{
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	pq.push({0, s});
	while (pq.size())
	{
		auto t = pq.top();
		pq.pop();
		int u = t.second;
		for (int i = 0; i < G[u].size(); i ++)
		{
			int v = G[u][i].v, w = G[u][i].w;
			if (vis[v])								// 一个队列中某个点可能会多次出现  只会取其中 d 最小的去更新  其余的都不会用到 
				continue;
			vis[v] = 1;
			if(d[u] + w < d[v])
			{
				d[v] = d[u] + w;
				pre[v] = u;							// 记录当前点的前驱节点 
				pq.push({-d[v], v});
			}
		}
	}
}

void path(int u)
{
	if (u == s)
	{
		cout << u << " ";
		return;
	}
	path(pre[u]);									// 递归输出路径 
	cout << u << " ";
}

int main()
{
	cin >> n >> m >> s;
	for (int i = 1; i <= m; i ++)
	{
		int u, v, w;
		cin >> u >> v >> w;
		G[u].push_back({v, w});
	}
	dijkstra(s);
	path(n);
 	return 0;
}

二. 存在负权边(无 bellmanford) 仅介绍 spfa   o ( m )

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

struct node
{
	int v, w;
};

const int N = 5e4 + 5, INF = 0x3f3f3f3f;
int n, m, s, ans, d[N], cnt[N], vis[N], pre[N];				
queue<int> q;										// 队列里存的是有资格去更新其他的点的点 
vector<node> G[N];

bool spfa(int s)
{
	memset(d, 0x3f, sizeof(d));
	d[s] = 0, vis[s] = 1, q.push(s);
	while (q.size())
	{
		int u = q.front();
		q.pop(), vis[u] = 0;
		for (int i = 0; i < G[u].size(); i ++)
		{
			int v = G[u][i].v, w = G[u][i].w;
			if (d[u] + w < d[v])
			{
				d[v] = d[u] + w;
				cnt[v] = cnt[u] + 1;				// 能到达当前点的点数加一 
				pre[v] = u;                         // 记录前驱结点
			}
			if (cnt[v] >= n)						// 共有n个点 但能到达当前点的点数超过了n 
				return true;						// 有负环 
			if (!vis[v])							// 只有本轮被更新过的点下一轮才有可能去更新其他的点 
				q.push(v), vis[v] = 1;				// 如果该点还不在队列中 就加入队列 
		}
	}
	return false;									// 无负环 
}

void path(int u)
{
	if (u == s)
	{
		cout << u << " ";
		return;
	}
	path(pre[u]);
	cout << u << " ";
}

int main()
{
	cin >> n >> m >> s;
	for (int i = 1; i <= m; i ++)
	{
		int u, v, w;
		cin >> u >> v >> w;
		G[u].push_back({v, w});
	}
	spfa(s);
	path(n);
 	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值