【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");
	}
}
posted @ 2020-12-02 16:59  stoorz  阅读(93)  评论(0编辑  收藏  举报