【2020.8.23NOIP模拟赛】最优路线
题目描述
一个 n 个点 m 条边的无重边无自环的无向图,点有点权,边有
边权,定义一条路径的权值为路径经过的点权的最大值乘边权最大
值。求任意两点间的权值最小的路径的权值。
输入
第一行两个整数 n,m,分别表示无向图的点数和边数。
第二行 n 个正整数,第 i 个正整数表示点 i 的点权。
接下来 m 行每行三个正整数 ui,vi,wi,分别描述一条边的两个端
输出
n 行每行 n 个整数,第 i 行第 j 个整数表示从 i 到 j 的路径的最小
权值,如果从 i 不能到达 j,则该值为-1。特别地,当 i=j 时输出 0。
输入样例复制
3 3
2 3 3
1 2 2
2 3 3
1 3 1
输出样例复制
0 6 3
6 0 6
3 6 0
说明
对于 20%的数据,n<=5,m<=8。
对于 50%的数据,n<=50
对于 100%的数据,n<=500,m<=n*(n-1)/2,边权和点权不超过 10^9。
解题思路
看到数据,我的第一反应是正解时间复杂度O(n2),然鹅正解竟然是O(n3)的Floyd!!!
这真的是惊到我了 (虽然比赛我用的就是这个,结果没有开long long,直接爆0,开了以后也才20分)
其实正解就是Floyd的变种,加一堆奇奇怪怪的东西(如:快读、O2、register等)就差不多能过了
下附代码:
#pragma GCC optimize(2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
long long c[510],b[510][510],d[510][510];
struct abc{
int x,y;
}a[100010];
bool hh(abc x,abc y)
{
return x.x<y.x;
}
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);
}
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,x,y,w;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[y][x])*max(c[x],c[y]);
}
sort(a+1,a+n+1,hh);
for(register int k=1;k<=n;k++)
{
register int s=a[k].y;
for(register int i=1;i<=n;i++)
for(register int j=1;j<=n;j++)
if(d[i][j]>max(d[i][s],d[s][j]))
{
d[i][j]=max(d[i][s],d[s][j]);
b[i][j]=min(b[i][j],d[i][j]*max(c[s],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]);
putchar(32);
}
printf("\n");
}
}