1. 程式人生 > 實用技巧 >【最短路 Floyd】最優路線

【最短路 Floyd】最優路線

題意

給定一張\(n\)個點\(m\)條邊的無向圖,無重邊無自環,每個點有點權,每條邊有邊權。
我們定義一條路徑的權值,為這條路徑經過的點權最大值乘以邊權最大值
對於每個點對\((u,v)\),你都要求出\(u,v\)之間的權值最小的路徑的權值。

資料範圍:\(1 \leq n \leq 500,0 \leq m \leq \frac{n\times (n-1)}2\),邊權和點權不超過\(10^9\)

思路

看到資料範圍很容易聯想到\(floyd\)
如果限定點權從小到大,只有不超過這個點權的點能經過,用限定的這個點權大小更新答案。時間複雜度是\(O(n^4)\)的。
考慮\(floyd\)的本質:\(k,i,j\)

三層迴圈中,\(f_{i,j}\)表示的是經過編號不超過\(k\)的點時\(i,j\)的最短路。
對於本題,可以讓\(k\)按點權從小到大列舉點,即限定了點權從小到大,這樣做就是\(floyd\)的複雜度了。

程式碼

#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long

struct node {
	int v, id;
}a[501];
int n, m;
int val[501], f[501][501], ans[501][501];

bool cmp(node x, node y) {
	return x.v < y.v;
}

signed main() {
	freopen("path.in", "r", stdin);
	freopen("path.out", "w", stdout);
	scanf("%lld %lld", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &a[i].v), a[i].id = i, val[i] = a[i].v;
	memset(f, 127 / 3, sizeof(f));
	memset(ans, 127 / 3, sizeof(ans));
	for (int i = 1, x, y, w; i <= m; i++) {
		scanf("%lld %lld %lld", &x, &y, &w);
		f[x][y] = f[y][x] = w;
		ans[x][y] = ans[y][x] = w * std::max(val[x], val[y]);
	}
	std::sort(a + 1, a + n + 1, cmp);
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				if (i != j)
					if (f[i][j] > std::max(f[i][a[k].id], f[a[k].id][j]))
						f[i][j] = std::max(f[i][a[k].id], f[a[k].id][j]),
						ans[i][j] = std::min(ans[i][j], f[i][j] * std::max(val[a[k].id], std::max(val[i], val[j])));
	for (int i = 1; i <= n; i++, printf("\n"))
		for (int j = 1; j <= n; j++)
			if (i == j)
				printf("0 ");
			else
				if (ans[i][j] == 707406378)
					printf("-1 ");
			else 
				printf("%lld ", ans[i][j]);
}