多源最短路是什么???
多源最短路,这个名字听起来好拉风啊
不过,它讲述的却很简单
就是随便 (sb) 选两个点,找出他们的最短距离
现在,有请我们的最佳算法得主 F l o y d Floyd Floyd
F l o y d Floyd Floyd
其实
F
l
o
y
d
Floyd
Floyd的本质就是
d
p
dp
dp
先来看一下他们的时空复杂度
- 时间复杂度
O ( n 3 ) O(n^3) O(n3) - 空间复杂度
S ( n 2 ) S(n^2) S(n2)
不过, S ( n 2 ) S(n^2) S(n2)似乎很复杂,我们先升一维
变成 f l k , x , y fl_{k,x,y} flk,x,y表示可以经过 1 ∼ k 1\sim k 1∼k的点,从 x x x到 y y y的最短距离
转移方程
f
l
k
,
x
,
y
=
m
i
n
{
f
l
k
−
1
,
.
x
,
y
,
f
l
k
−
1
,
x
,
k
+
f
l
k
−
1
,
k
,
y
}
fl_{k,\ x,\ y}=min\{fl_{k-1,\ .x,\ y},\ fl_{k-1,\ x,\ k}+fl_{k-1,\ k,\ y}\}
flk, x, y=min{flk−1, .x, y, flk−1, x, k+flk−1, k, y}
我们可以画图
.
.
..
.. o a
.
.
. 2| \1
.
.
..
.. o–o
.
.
..
.. b4c
a
=
1
,
b
=
2
,
c
=
3
a=1,b=2,c=3
a=1,b=2,c=3
我们现在已经开通了线路1(
b
∼
c
b\sim c
b∼c)
f
l
0
,
2
,
3
=
4
fl_{0,2,3}=4
fl0,2,3=4,就是从2到3直达,接着我们再拿来a,也就是线路2(
b
∼
a
∼
c
b \sim a \sim c
b∼a∼c),那么
f
l
1
,
2
,
3
=
m
i
n
{
f
l
1
−
1
,
.
2
,
3
,
f
l
1
−
1
,
2
,
1
+
f
l
1
−
1
,
1
,
3
}
fl_{1,\ 2,\ 3}=min\{fl_{1-1,\ .2,\ 3},\ fl_{1-1,\ 2,\ 1}+fl_{1-1,\ 1,\ 3}\}
fl1, 2, 3=min{fl1−1, .2, 3, fl1−1, 2, 1+fl1−1, 1, 3}
我们也知道
f
l
1
−
1
,
2
,
1
=
2
,
f
l
1
−
1
,
1
,
3
=
1
fl_{1-1,\ 2,\ 1}=2,\ fl_{1-1,\ 1,\ 3}=1
fl1−1, 2, 1=2, fl1−1, 1, 3=1
那么整个算式便是
f
l
1
,
2
,
3
=
m
i
n
{
4
,
2
+
1
}
fl_{1,\ 2,\ 3}=min\{4,\ 2+1\}
fl1, 2, 3=min{4, 2+1}
那么明显
f
l
1
,
2
,
3
=
3
fl_{1,\ 2,\ 3}=3
fl1, 2, 3=3,我们再用眼睛验算一下,对的,结束
F
l
o
y
d
Floyd
Floyd算法的正确性已被证明,那么降维呢?不着急,咱们先看一下代码
#include<bits/stdc++.h>
#define int long long //不开long long见祖宗(bushi)
#define N 507
#define min(a,b) a<b?a:b
int fl[N][N][N],n,m,u,v,wt;//dp、有… …
void Floyd_like(int n){//开始咯
for(int k=1;k<=n;k++)
for(int x=1;x<=n;x++)
for(int y=1;y<=n;y++)
fl[k][x][y]=min(fl[k-1][x][y],fl[k-1][x][k]+fl[k-1][k][y]);//转移方程
}
signed main(){
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++){
fl[0][i][i]=0;
for(int j=i+1;j<=n;j++)
fl[0][j][i]=fl[0][i][j]=0x3f3f3f3f;
}
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&wt);
/*fl[0][v][u]=*/fl[0][u][v]=wt;//注,本图为有向图,如果是无向图的话把那对/**/拆了
}
Floyd_like(n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j)
continue;
printf("from: %lld ",i);
printf("to: %lld ",j);
printf("distance: ");
if(fl[n][i][j]!=0x3f3f3f3f)
printf("%lld\n",fl[n][i][j]);
else
puts("can't arrive!");
}
return 0;
}
/*
测试样例
3 3
1 2 3
2 3 4
1 3 5
*/
然后我们发现, k k k其实是个多余的东西,扔了它
那么如果 f l k − 1 , x , k fl_{k-1,\ x,\ k} flk−1, x, k、 f l k − 1 , k , y fl_{k-1,\ k,\ y} flk−1, k, y已经更新了呢?
这个不用急,因为他们本身就是经过 k k k的,而且进一步更新不更好吗(虽然没用)
降维
#include<bits/stdc++.h>
#define int long long //不开long long见祖宗(bushi)
#define N 507
#define min(a,b) a<b?a:b
int fl[N][N],n,m,u,v,wt;//dp、有… …
void Floyd_like(int n){//开始咯
for(int k=1;k<=n;k++)
for(int x=1;x<=n;x++)
for(int y=1;y<=n;y++)
fl[x][y]=min(fl[x][y],fl[x][k]+fl[k][y]);//转移方程
}
signed main(){
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++){
fl[i][i]=0;
for(int j=i+1;j<=n;j++)
fl[j][i]=fl[i][j]=0x3f3f3f3f;
}
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&wt);
/*fl[v][u]=*/fl[u][v]=wt;//注,本图为有向图,如果是无向图的话把那对/**/拆了
}
Floyd_like(n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j)
continue;
printf("from: %lld ",i);
printf("to: %lld ",j);
printf("distance: ");
if(fl[i][j]!=0x3f3f3f3f)
printf("%lld\n",fl[i][j]);
else
puts("can't arrive!");
}
return 0;
}
/*
测试样例
3 3
1 2 3
2 3 4
1 3 5
*/
小试牛刀
地址
【模板】floyd
题目背景
模板题,无背景
题目描述
给出n个点,m条边的无向图,求每个点到其他点的距离之和 % 998244354 \% 998244354 %998244354的值
输入格式
第一行两个数n,m含义如上
从第二行开始,共m行,每行三个数x,y,l,代表从x到y点的长度为l
输出格式
n行,每行一个数,第i行代表点i到其他点的距离之和
样例 #1
样例输入 #1
2 1
1 2 4
样例输出 #1
4
4
样例 #2
样例输入 #2
4 5
1 2 1
1 3 2
2 3 2
3 4 3
2 4 4
样例输出 #2
8
7
7
12
提示
模板题,保证图联通
n<=500
m<=10000
1<=x,y<=n
l<=1e9
现在,把原来的 c o p y copy copy过来
#include<bits/stdc++.h>
#define me 998244354
#define int long long //不开long long见祖宗(bushi)
#define N 507
#define min(a,b) a<b?a:b
int fl[N][N],n,m,u,v,wt;//dp、有… …
void Floyd_like(int n){//开始咯
for(int k=1;k<=n;k++)
for(int x=1;x<=n;x++)
for(int y=1;y<=n;y++)
fl[x][y]=min(fl[x][y],fl[x][k]+fl[k][y]);//转移方程
}
signed main(){
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++){
fl[i][i]=0;
for(int j=i+1;j<=n;j++)
fl[j][i]=fl[i][j]=0x3f3f3f3f;
}
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&wt);
fl[v][u]=fl[u][v]=wt;
}
Floyd_like(n);
for(int i=1;i<=n;i++){
int ans=0;
for(int j=1;j<=n;j++)
(ans+=fl[i][j])%=me;
printf("%d\n",me);
}
return 0;
}
好了,去吧,皮卡丘!