最短路总结

一,求多源最短路(即任意两点间的最短路)
  Floyd: 时间复杂度为O(n^3),Floyd还可以用于计算传递闭包和无向图最小环,是一个值得记住的算法。

void floyd()
{
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

二,求给定起点到任意点的最短路
  1.SPFA
  SPFA一般只用于费用流和判断负环,没有负环尽量不使用SPFA**,时间复杂度为O(km), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2(不存在环的情况下),m为边的数量。
  其中判断负环的又分为几种情况:
 (1)从起点到终点的路上有负环,最短路为负无穷。
 (2)从起点可以到负环,但是一旦进入负环便无法到达终点,所以最短路存在。
 (3)存在负环,但是从起点无法到达。

void spfa(int s)//s为起点
{
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[s] = 0;
	queue<int> q;
	q.push(s);
	while (q.size())
	{
		int x = q.front();
		q.pop();
		v[x] = 0;
		for (int i = head[x]; i; i = nex[i])
		{
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z)
			{
				d[y] = d[x] + z;
				if (!v[y])
				{
					q.push(y);
					v[y] = 1;
				}
			}
		}
	}
}

利用SPFA算法判断有没有环(正环和负环都行):

void spfa(int s)//s为起点
{
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[s] = 0;
	queue<int> q;
	q.push(s);
	while (q.size())
	{
		int x = q.front();
		q.pop();
		v[x] = 0;
		for (int i = head[x]; i; i = nex[i])
		{
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z)
			{
				d[y] = d[x] + z;
				cnt[y] = cnt[x] + 1;//cnt[y]表示起点到y的最短路径包含的边数
				if (cnt[y] >= n)//n为点数
				{
					puts("存在负环");
					return;
				}
				if (!v[y])
				{
					q.push(y);
					v[y] = 1;
				}
			}
		}
	}
}

spfa算法的SLF优化:

void spfa(int s)//s为起点
{
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[s] = 0;
	deque<int> q;
	q.push_front(s);
	while (q.size())
	{
		int x = q.front();
		q.pop_front();
		v[x] = 0;
		for (int i = head[x]; i; i = nex[i])
		{
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z)
			{
				d[y] = d[x] + z;
				if (!v[y])
				{
					if (q.size() && d[y] < d[q.front()])
						q.push_front(y);
					else
						q.push_back(y);
					v[y] = 1;
				}
			}
		}
	}
}

2.迪杰斯特拉算法(Dijkstra)
  基于贪心的思想,不能处理负权的情况,每个点额外维护一个属性,表示是否确定了最短路。 时间复杂度为O( (m + n) log(n) ),迪杰斯特拉算法不能判断环的存在。

void dij(int s)//s为起点
{
	//
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[s] = 0;
	priority_queue< pair<int, int> > q;//pair第一维为d的相反数,第二维为节点编号
	q.push(make_pair(0, s));
	while (q.size())
	{
		int x = q.top().second;//每次取d最小的编号
		q.pop();
		if (v[x])
			continue;
		v[x] = 1;
		for (int i = head[x]; i; i = nex[i])
		{
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z)
			{
				d[y] = d[x] + z;
				q.push(make_pair(-d[y], y));
			}
		}
	}
}

三:
其他变形
可能需要求解
最长路。把所有边权取相反数即可
乘积最大。边权相乘,可以通过取对数转化为加法。
二分查找答案。
反向建边。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值