【YbtOJ#20238】最优路线
题目
题目链接:https://www.ybtoj.com.cn/contest/68/problem/2
给定一张 \(n\) 个点 \(m\) 条边的无向图,无重边无自环,每个点有点权,每条边有边权。
我们定义一条路径的权值,为这条路径经过的点权最大值乘以边权最大值。
对于每个点对 \((u,v)\),你都要求出 \(u,v\) 之间的权值最小的路径的权值。
思路
按点权从小到大枚举点。
假设当前枚举到点 \(x\),从 \(x\) 开始跑一遍 Prim,求出 \(x\) 到每一个点的路径中路径长度最小值。
然后枚举所有点对 \((i,j)\),用 \(x\) 去更新 \((i,j)\) 的贡献,显然是 \(\max(mind[i],mind[j])\times a[x]\)。
时间复杂度 \(O(n^3)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=510;
int n,m,pre[N],d[N],mind[N],dis[N][N];
ll ans[N][N];
bool vis[N],flag[N];
struct node
{
int a,id;
}a[N];
bool cmp(node x,node y)
{
return x.a<y.a;
}
void prim(int s)
{
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
memset(mind,0,sizeof(mind));
memset(d,0x3f3f3f3f,sizeof(d));
d[s]=0;
for (int k=1;k<=n;k++)
{
int p=0;
for (int i=1;i<=n;i++)
if (flag[i] && !vis[i] && (d[i]<d[p] || !p)) p=i;
vis[p]=1; mind[p]=max(d[p],mind[pre[p]]);
for (int i=1;i<=n;i++)
if (flag[i] && !vis[i] && d[i]>dis[i][p])
d[i]=dis[i][p],pre[i]=p;
}
}
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(ans,0x3f3f3f3f,sizeof(ans));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i].a);
a[i].id=i;
}
for (int i=1,x,y,z;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
dis[x][y]=dis[y][x]=z;
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
int x=a[i].id;
flag[x]=1;
prim(x);
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
if (flag[j] && flag[k])
ans[j][k]=ans[k][j]=min(ans[j][k],1LL*max(mind[j],mind[k])*a[i].a);
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
if (ans[i][j]<1e18) printf("%lld ",ans[i][j]);
else printf("-1 ");
printf("\n");
}
}