【2020.8.23 NOIP模拟赛】最优路线【Floyed+DP】

67 篇文章 0 订阅
66 篇文章 1 订阅

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


输入格式
第一行两个整数 n,m,分别表示无向图的点数和边数。

第二行 n 个正整数,第 i 个正整数表示点 i 的点权。

接下来 m 行每行三个正整数 u i , v i , w i u_i,v_i,w_i ui,vi,wi,分别描述一条边的两个端点和边权。

输出格式
n 行每行 n 个整数,第 i 行第 j 个整数表示从 i i i j j j 的路径的最小权值,如果从 i 不能到达 j,则该值为 − 1 −1 1。特别地,当 = j =j =j时输出 0 0 0


输入输出样例

输入 #1复制
3 3
2 3 3
1 2 2
2 3 3
1 3 1
输出 #1复制
0 6 3
6 0 6
3 6 0


说明/提示
对于 20 20 20% 的数据, n ≤ 5 , m ≤ 8 n≤5,m≤8 n5,m8

对于 50 50 50% 的数据, n ≤ 50 n≤50 n50

对于 100 100 100% 的数据, n ≤ 500 , m ≤ n ( n − 1 ) / 2 n≤500,m≤n(n−1)/2 n500,mn(n1)/2,边权和点权不超过 1 0 9 10^9 109


解题思路
这道题是 弗洛伊德算法 的改装版。。。

我们定义一个叫做 d i s [ ] [ ] dis[][] dis[][]数组的东西作为我们的转移变量,存储的是从i号点到j号点的最小的最大路径长度乘上路径上的最大点权.

我们先对点权进行从小到大的排序,那么我们就可以保证我们每一次加进去的点(作为我们 F l o y d Floyd Floyd算法的中转点,都是最优秀的,当然我们对于该条路径上的最大值一定还是要和该路径上两边的点的点权进行比较)。。。


代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#pragma GCC optimize(2)
using namespace std;
int n,m;
long long w,x,y,b[510][510],c[510],d[510][510];//b[][]就是两点间的权值最小的路径的权值,额,通俗讲就是我们在求的东东;  c[]是每个点的点权。 ; d[][]是两点间最优路径经过的边权最大值
int read()
{
	register int X=0;register bool flag=1; register char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
struct cc{
	int x,y;
}a[100010];
bool cmp(const cc&l,const cc&r)
{return l.x<r.x;}
int main(){
	n=read();
	m=read();
	memset(d,127,sizeof(d));
	memset(b,127,sizeof(b));
	for(register int i=1;i<=n;i++)
	{
		c[i]=read();
		b[i][i]=0;
		a[i].x=c[i];
		a[i].y=i; 
	}
	for(register int i=1;i<=m;i++)
	{
		x=read();
		y=read();
		w=read();
		d[x][y]=d[y][x]=w;
		b[x][y]=b[y][x]=min(b[x][y],d[x][y]*max(c[x],c[y]));
	}
	sort(a+1,a+n+1,cmp);//对点权进行从小到大的排序
	for(register int k=1;k<=n;k++)//Floyed ,k枚举中间点(register玄学卡常)
	{
		long long bsy=a[k].y;
		for(register int i=1;i<=n;i++)
		{
			for(register int j=1;j<=n;j++)
			{
			//	if((i!=j)&&(j!=bsy)&&(bsy!=i))
			//	{
					if(d[i][j]>max(d[i][bsy],d[bsy][j]))//如果当前这条路径经过的边权最大值更小,也就是这条路更优,就更新。。
					{
						d[i][j]=max(d[i][bsy],d[bsy][j]);
						b[i][j]=min(b[i][j],d[i][j]*max(c[bsy],max(c[i],c[j])));
					} 
			//	}
			}
		}
	}
	for(register int i=1;i<=n;i++)
	{
		for(register int j=1;j<=n;j++)
		{
			if(i==j) printf("0 ");	
			else if(d[i][j]==0) printf("-1 ");
			else printf("%lld ",b[i][j]);
		}
		printf("\n");
	}
}



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值