2021-08-13

最短路径
练习
Dijkstra 邻接矩阵版

#include<cstdio>
#include<algorithm>
using namespace std;
const int Inf = 1000000;
const int maxv = 550;
int G[maxv][maxv], n, num[maxv] = { 0 }, weight[maxv] = { 0 }, d[maxv], w[maxv] = {0};
bool vis[maxv] = { false };
void Dijsktra(int v)
{
	
	fill(d, d + maxv, Inf);
	d[v] = 0;
	num[v] = 1;
	w[v] = weight[v];
	for (int i = 0; i < n; i++)
	{
		int u = -1, min = Inf;
		for (int j = 0; j < n; j++)
		{
			if (d[j] < min&&vis[j]==false)
			{
				u = j;
				min = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int j = 0; j < n; j++)
		{
			if (vis[j] == false && G[u][j] != Inf && d[u] + G[u][j] < d[j])
			{
				d[j] = d[u] + G[u][j];
				w[j] = w[u] + weight[j];
				num[j] = num[u];
			}
			else if (d[j] == d[u] + G[u][j])
			{
				if (w[u] + weight[j] > w[j])
				{
					w[j] = w[u] + weight[j];
				}
				num[j] += num[u];
			}
		}
	}
}
int main()
{
	int m, c1, c2;
	fill(G[0], G[0] + maxv * maxv, Inf);
	scanf("%d%d%d%d", &n, &m, &c1, &c2);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &weight[i]);
	}
	for (int i = 0; i < m; i++)
	{
		int t1, t2, length;
		scanf("%d%d%d", &t1, &t2, &length);
		G[t1][t2] = G[t2][t1] = length;
	}
	Dijsktra(c1);
	printf("%d %d", num[c2], w[c2]);
	return 0;
}

Dijkstra 邻接表版

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxv = 510;
const int Inf = 0x3fffffff;
struct Node {
	int v, s;
	Node(int a, int b) :v(a), s(b) {}
};
vector<Node >Adj[maxv];
int d[maxv], weight[maxv] = { 0 }, num[maxv] = {0},n,w[maxv];
bool vis[maxv] = { false };
void Dijkstra(int s)
{
	fill(d, d + maxv, Inf);
	d[s] = 0;
	num[s] = 1;
	w[s] = weight[s];
	for (int i = 0; i < n; i++)
	{
		int u = -1, min = Inf;
		for (int j = 0; j < n; j++)
		{
			if (vis[j] == false && d[j] < min)
			{
				u = j;
				min = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int j = 0; j < Adj[u].size(); j++)
		{
			if (vis[Adj[u][j].v] == false)
			{
				if (d[Adj[u][j].v] > d[u] + Adj[u][j].s)
				{
					d[Adj[u][j].v] = d[u] + Adj[u][j].s;
					w[Adj[u][j].v] = w[u] + weight[Adj[u][j].v];
					num[Adj[u][j].v] = num[u];
				}
				else if (d[Adj[u][j].v] == d[u] + Adj[u][j].s)
				{
					if (w[Adj[u][j].v] < w[u] + weight[Adj[u][j].v])
					{
						w[Adj[u][j].v] = w[u] + weight[Adj[u][j].v];
					}
					num[Adj[u][j].v] += num[u];
				}
			}
		}
	}
}
int main()
{
	int m, s, end;
	scanf("%d%d%d%d", &n, &m, &s, &end);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", weight + i);
	}
	for (int i = 0; i < m; i++)
	{
		int t1, t2, temp;
		scanf("%d %d %d",&t1,&t2,&temp);
		Node temp1 = Node(t1, temp);
		Node temp2 = Node(t2, temp);
		Adj[t1].push_back(temp2);
		Adj[t2].push_back(temp1);
	}
	Dijkstra(s);
	printf("%d %d", num[end], w[end]);
	return 0;
}

差别邻接表相较于邻接矩阵而言在无需判断两个顶点是否相通,当题目有与点权有关的数据时邻接矩阵必须用结构体,若不要求点权可以当作int型数组。
Bellmen-Ford算法
对所有边进行n-1次便利,理论上经过n-1便利后所有顶点的最短路径都确定了,如果再对所有边进行遍历若出现更新最短路径的情况,则说明有源点可达的负环。
例题pat1003
代码:

#include<cstdio>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
const int maxv = 520;
const int Inf = 0x3fffffff;
struct Node {
	int v, s;
	Node(int a, int b) :v(a), s(b) {}
};
vector<Node> Adj[maxv];
int n, num[maxv] = { 0 }, d[maxv], w[maxv] = {0}, weight[maxv] = { 0 };
set<int >pre[maxv];
bool Bellman(int s)
{
	fill(d, d + maxv, Inf);
	d[s] = 0;
	num[s] = 1;
	w[s] = weight[s];
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n; j++)
		{
			for (int k = 0; k < Adj[j].size(); k++)
			{
				if (d[Adj[j][k].v] > d[j] + Adj[j][k].s)
				{
					d[Adj[j][k].v] = d[j] + Adj[j][k].s;
					w[Adj[j][k].v] = w[j] + weight[Adj[j][k].v];
					num[Adj[j][k].v] = num[j];//更新最短路径无需重新计算路径数
					pre[Adj[j][k].v].clear();
					pre[Adj[j][k].v].insert(j);

				}
				else if (d[Adj[j][k].v] == d[j] + Adj[j][k].s)
				{
					if (w[Adj[j][k].v] < w[j] + weight[Adj[j][k].v])
					{
						w[Adj[j][k].v] = w[j] + weight[Adj[j][k].v];
					}
					pre[Adj[j][k].v].insert(j);
					set<int>::iterator it;//出现相同长度的最短路径重新计算最短路径的数量
					num[Adj[j][k].v] = 0;
					for (it = pre[Adj[j][k].v].begin(); it != pre[Adj[j][k].v].end(); it++)
					{
						num[Adj[j][k].v]+=num[*it];//当前结点最短路径数等于各前驱结点的最短路径数之和
					}
				}
				
			}
		}
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < Adj[i].size(); j++)
		{
			if (d[Adj[i][j].v] > d[i] + Adj[i][j].s)
			{
				return false;
			}
		}
	}
	return true;
}
int main()
{
	int m, s, end;
	bool mark;
	scanf("%d%d%d%d", &n, &m, &s, &end);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", weight + i);
	}
	for (int i = 0; i < m; i++)
	{
		int v1, v2, s;
		scanf("%d%d%d", &v1, &v2, &s);
		Node temp1 = Node(v1, s);
		Node temp2 = Node(v2, s);
		Adj[v1].push_back(temp2);
		Adj[v2].push_back(temp1);
	}
	mark=Bellman(s);
	printf("%d %d", num[end], w[end]);

	return 0;
}

要点
1Bellman算法可以判断是否存在负环所以一般函数输出为bool类型。
2Dijkstra算法之所以无法处理负环是应为,Dijkstra算法再访问完一个节点后该结点的最短路径就确定了,而若存在负环,此时所确定的最短路径不一定是最短路径。
3对于人一个非源顶点其最短路径数等于其最短路径上所有前驱顶点的路径数之和
4Bellman的最短路径数的计算要用set,原因在于,其会多次访问同一个顶点,而set是存储不重复的有序的容器。
5若用邻接矩阵实现Bellman,时间复杂度高达n^3,所以更加适合用邻接表
SPFA(Bellman的优化版)
思想:只有一个顶点的最短路径更新时,才需要更新经过其的最短路径。
实现,用一个队列来装需要更新的顶点,当顶点更新完了以后,将可以通过它到达的顶点也入队更新最短路径,如此重复直到队列为空或是某点入队次数超过了n-1(n为顶点数)。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
using namespace std;
const int maxv = 550;
const int Inf = 0x3fffffff;
struct Node {
	int v, s;
	Node(int _v, int _s) : v(_v), s(_s) {}
};
vector<Node >Adj[maxv];
queue<int>Q;
bool inq[maxv] = { false };
int d[maxv], num[maxv] = { 0 }, mark[maxv] = { 0 }, w[maxv] = { 0 }, weight[maxv] = {0},n;
set<int> pre[maxv];
bool SPFA(int s)
{
	Q.push(s);
	fill(d, d + maxv, Inf);
	inq[s] = true;
	mark[s]++;
	d[s] = 0;
	num[s] = 1;
	w[s] = weight[s];
	while (!Q.empty())
	{
		int top = Q.front();
		Q.pop();
		inq[top] = false;
		for (int i = 0; i < Adj[top].size(); i++)
		{
			if (d[Adj[top][i].v] > d[top] + Adj[top][i].s)
			{
				d[Adj[top][i].v] = d[top] + Adj[top][i].s;
				num[Adj[top][i].v] = num[top];
				w[Adj[top][i].v] = w[top] + weight[Adj[top][i].v];
				pre[Adj[top][i].v].clear();
				pre[Adj[top][i].v].insert(top);
				if (inq[Adj[top][i].v] == false)
				{
					Q.push(Adj[top][i].v);
					inq[Adj[top][i].v] = true;
					mark[Adj[top][i].v]++;
				}
			}
			else if (d[Adj[top][i].v] == d[top] + Adj[top][i].s)
			{
				if (w[Adj[top][i].v] < w[top] + weight[Adj[top][i].v])
				{
					w[Adj[top][i].v] = w[top] + weight[Adj[top][i].v];
				}
				pre[Adj[top][i].v].insert(top);
				num[Adj[top][i].v] = 0;
				set<int> ::iterator it;
				for (it = pre[Adj[top][i].v].begin(); it != pre[Adj[top][i].v].end(); it++)
				{
					num[Adj[top][i].v] += num[*it];
				}
			}
			if (mark[Adj[top][i].v] >= n) return false;
		}
	}
	return true;
}
int main()
{
	int m, s, end;
	scanf("%d%d%d%d", &n, &m, &s, &end);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", weight + i);
	}
	for (int i = 0; i < m; i++)
	{
		int v1, v2, temp;
		scanf("%d%d%d", &v1, &v2, &temp);
		Node temp1 = Node(v1, temp);
		Node temp2 = Node(v2, temp);
		Adj[v1].push_back(temp2);
		Adj[v2].push_back(temp1);
	}
	SPFA(s);
	printf("%d %d", num[end], w[end]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值