Floyd算法


题目链接:https://www.acwing.com/problem/content/856/

题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,边权可能为负数。
再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,则输出 impossible。
数据保证图中不存在负权回路。

输入描述

第一行包含三个整数 n,m,k。
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
接下来 k 行,每行包含两个整数 x,y,表示询问点 x 到点 y 的最短距离。

输出描述

共 k 行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible。
1≤n≤200
1≤k≤n2
1≤m≤20000
图中涉及边长绝对值均不超过 10000。

示例

输入

3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3

输出

impossible
1

Floyd算法

算法介绍

Floyd算法是求多源最短路的经典算法,可以有负权边,但不能有负权回路

该算法采用了动态规划的思想。

状态表示

f(k,i,j)表示从i到j,且途径的点的编号不大于k的最短路径。

状态转移方程

f(k,i,j) = min(f(k-1,i,j), f(k-1,i,k) + f(k-1,k,j))

观察该方程可以发现:

  • f(k,i,k) = f(k-1,i,k) 恒成立
  • f(k,k,j) = f(k-1,k,j) 恒成立

因此我们不必存储每一轮得计算结果(由上述两式知不存在数据覆盖的情况),则该方程可以优化降维:

f(i,j) = min(f(i,k) + f(k,j)) k=1,2,···,n

本题代码处理还存在一些细节,详见注释。

AC代码


#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>

#define INF 0x3f3f3f3f

using namespace std;
const int N = 210;
int n, m, q;
int d[N][N];

void floyd()
{
	/* 注意负权边和INF是可以更新d[i][j]的
	 * 但其仍然是不可达的
	 * 因此最后注意对这一情况的处理*/
	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]);
}

int main()
{
	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (i == j)
				d[i][j] = 0;
			else
				d[i][j] = INF;

	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		d[a][b] = min(d[a][b], c);
	}

	floyd();

	while (q--)
	{
		int a, b;
		cin >> a >> b;
		/* 负权边的存在,可能会让d[a][b]小于INF
		 * 且图中边的长度小于10000
		 * 因此用INF/2来处理这种情况即可*/
		if (d[a][b] < INF / 2)	
			cout << d[a][b] << '\n';
		else
			cout << "impossible\n";
	}
	return 0;
}

文章作者: Kong Aobo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kong Aobo !
  目录