[2018.10.17 T2] 最优路线

本文探讨了在一个带有点权和边权的无向图中寻找任意两点间权值最小路径的算法。通过改进的Floyed算法,按点权排序并更新路径上的最大边权和最大点权,最终求得所有点对之间的最小权值路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暂无链接

最优路线

【题目描述】

一个nnn个点mmm条边的无重边无自环的无向图,点有点权,边有边权,定义一条路径的权值为路径经过的点权的最大值乘边权最大值。求任意两点间的权值最小的路径的权值。

【输入格式】

第一行两个整数n,mn,mn,m,分别表示无向图的点数和边数。
第二行nnn个正整数,第iii个正整数表示点iii的点权。
接下来mmm行每行三个正整数ui,vi,wiu_i,v_i,w_iui,vi,wi,分别描述一条边的两个端点和边权。

【输出格式】

nnn行每行nnn个整数,第iii行第jjj个整数表示从iiijjj的路径的最小权值,如果从iii不能到达jjj,则该值为−1-11。特别地,当i=ji=ji=j时输出000

【样例 1】
path. in

3 3
2 3 3
1 2 2
2 3 3
1 3 1

path.out

0 6 3
6 0 6
3 6 0

【样例 2】

见选手目录下 path. in/path.ans。

【数据范围与约定】

对于20%20\%20%的数据,n&lt;=5,m&lt;=8n&lt;=5,m&lt;=8n<=5,m<=8
对于50%50\%50%的数据,n&lt;=50n&lt;=50n<=50
对于100%100\%100%的数据,n&lt;=500,m&lt;=n∗(n−1)/2n&lt;=500,m&lt;=n*(n-1)/2n<=500,m<=n(n1)/2,边权和点权不超过10910^9109

题解

先考虑505050分做法,dp[i][j][k]dp[i][j][k]dp[i][j][k]表示iiijjj,路径上最长边长度为kkk时最大点点权的最小值,类似Floyed\mathcal{Floyed}Floyed更新一下就可以做到O(n4)O(n^4)O(n4)

事实上,我们不需要那么麻烦,mx[i][j]mx[i][j]mx[i][j]表示iiijjj路径上最长边的最小值,使用Floyed\mathcal{Floyed}Floyed来更新,但是枚举中继点kkk的时候我们要按点权从小到大枚举,这样iiijjj路径上点权最大的点就一定是i,j,ki,j,ki,j,k中的一个,于是便可以更新答案为min(ans,mx[i][j]×max(val[k],max(val[i],val[j]))min(ans,mx[i][j]\times max(val[k],max(val[i],val[j]))min(ans,mx[i][j]×max(val[k],max(val[i],val[j]))

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=505;
struct sd{int val,id;}pt[M];
bool operator<(sd a,sd b){return a.val<b.val;}
int val[M],mx[M][M],n,m;
ll ans[M][M];
void in()
{
	scanf("%d%d",&n,&m);
	memset(mx,127,sizeof(mx));
	for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(i!=j)ans[i][j]=LONG_LONG_MAX;
	for(int i=1;i<=n;++i)scanf("%d",&val[i]),pt[i]=(sd){val[i],i};
	for(int i=1,a,b,c;i<=m;++i)scanf("%d%d%d",&a,&b,&c),mx[a][b]=mx[b][a]=c,ans[a][b]=ans[b][a]=1ll*max(val[a],val[b])*mx[a][b];
}
void ac()
{
	sort(pt+1,pt+1+n);
	for(int k=1;k<=n;++k)for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
	if(mx[i][j]>max(mx[i][pt[k].id],mx[pt[k].id][j]))
	mx[i][j]=max(mx[i][pt[k].id],mx[pt[k].id][j]),ans[i][j]=min(ans[i][j],1ll*mx[i][j]*max(pt[k].val,max(val[i],val[j])));
	for(int i=1;i<=n;++i,putchar(10))for(int j=1;j<=n;++j)printf("%lld ",ans[i][j]==LONG_LONG_MAX?-1:ans[i][j]);
}
int main(){in(),ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值