「Luogu P?」传送门

Description

传智专修学院里有 \(n\) 栋教学楼,有 \(m\) 条双向通行道路连接这些教学楼,不存在重边和自环。每条道路都有一定的长度,而且所有教学楼之间都可以直接或者间接的通过道路到达。我们可以很容易的求出这些教学楼之间的最短路。

为了使交通更为顺畅,校方决定在两个教学楼里增设一对传送门。传送门可以将这对教学楼的距离直接缩短为 \(0\)。利用传送门,某些教学楼之间的最短路的距离就变短了。

由于预算有限,学校里只能安装一对传送门。但是校长希望尽可能方便学生,使任意两点之间的最短路长度的总和最小。当然啦,从 \(x\) 教学楼到 \(y\) 教学楼的长度和从 \(y\) 教学楼到 \(x\) 教学楼的长度只需要统计一次就可以了。

Hint

\(1\le n\le 10^2, 1\le m\le \dfrac{n(n-1)}{2}\)

Solution

因为一个zz错误痛失first blood

应该是比较简单的做法。

首先对原图跑一边多源最短路径算法,这里使用 Floyd。

之后,我们枚举所有可能的传送门的位置,即用一个 \(n^2\) 循环枚举端点。

在对于每一个传送门 \(i,j\),计算新的最短路的和。怎么计算新的最短路的和?

可以知道,对于两个不同的结点 \(x, y\) ,它们之间的新最短路要么就是原来的,否则就是 \(x,i\) 的最短路 加上 \(j,y\) 的最短路 与 \(x,j\) 的最短路 加上 \(i,y\) 的最短路。

那么只要选择小的那个即可。

时间复杂度 \(O(n^4)\)

Code

#include <iostream>
#include <climits>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 105;
long long dist[N][N];
int n, m;

signed main() {
	ios::sync_with_stdio(false);
	cin >> n >> m;
	memset(dist, 0x3f, sizeof dist);
	for (register int u, v, w, i = 1; i <= m; i ++)
		cin >> u >> v >> w, dist[u][v] = dist[v][u] = w;
	
	for (register int i = 1; i <= n; i ++)
		dist[i][i] = 0;
	for (register int k = 1; k <= n; k ++)
		for (register int i = 1; i <= n; i ++)
			for (register int j = 1; j <= n; j ++)
				if (i != j && i != k && j != k)
					dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
	
	long long ans = LLONG_MAX;
	for (register int i = 1; i <= n; i ++)
		for (register int j = i + 1; j <= n; j ++) {
			long long cur = 0ll;
			for (register int x = 1; x <= n; x ++)
				for (register int y = x + 1; y <= n; y ++)
					cur += min(dist[x][y], min(dist[x][i] + dist[j][y], dist[x][j] + dist[i][y])); //注意这里有两个 min
			ans = min(ans, cur);
		}
	cout << ans << endl;
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/-Wallace-/p/12727340.html