2021 辽宁省赛 B 阿强的路
阿强的路
弗洛伊德算法
一个路径的代价是这条路径的最大点权和最大边权的积
对于边权,我们用弗洛伊德来维护
对于点权,我们将其从小到大排序,弗洛伊德枚举中间点的时候就是从小到大
做这题的时候,发现对于弗洛伊德的理解还是不够透彻,其记忆化的方式在于,第一层循环的用意是,起点 -> 中间点 -> 终点,中间点仅由前 k 个点组成
因此弗洛伊德的转移为:当路径的最大边权变小时,尝试更新路径的最小代价,点权的最大值就是 起点、中间点、终点的点权最大值,即 \(max(a[i],a[k],a[j])\),因为点根据点权从小到大排,所以中间点的点权最大就为 \(a[k]\)
为何不尝试在边权变大的时候更新?因为是由点权从小到大排,因此对于同一起点和终点,他所搜索到的路径的最大点权只会越来越大
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 510;
typedef long long ll;
const ll inf = 1e18 + 10;
ll dis[maxn][maxn], ans[maxn][maxn], num[maxn];
struct node
{
ll val;
int id;
}x[maxn];
bool cmp(const node& a, const node& b)
{
return a.val < b.val;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++)
{
scanf("%lld", &x[i].val);
num[i] = x[i].val;
x[i].id = i;
}
for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) ans[i][j] = ans[j][i] = dis[j][i] = dis[i][j] = inf;
for(int i=0; i<m; i++)
{
ll a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
dis[a][b] = dis[b][a] = c;
ans[a][b] = ans[b][a] = min(ans[a][b], dis[a][b] * max(x[a].val, x[b].val));
}
sort(x + 1, x + 1 + n, cmp);
for(int kk=1; kk<=n; kk++)
{
int k = x[kk].id;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(i == j || k == i || k == j) continue;
ll temp = max(dis[i][k], dis[k][j]);
if(dis[i][j] > temp)
{
dis[i][j] = temp;
ans[i][j] = min(ans[i][j], dis[i][j] * max({num[i], num[j], num[k]}));
}
}
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(j != 1) printf(" ");
if(i == j) printf("0");
else if(ans[i][j] == inf) printf("-1");
else printf("%lld", ans[i][j]);
}
printf("\n");
}
return 0;
}