多源最短路

多源最短路是什么???

多源最短路,这个名字听起来好拉风啊
不过,它讲述的却很简单
就是随便 (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
先来看一下他们的时空复杂度

  1. 时间复杂度
    O ( n 3 ) O(n^3) O(n3)
  2. 空间复杂度
    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 1k的点,从 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{flk1, .x, y, flk1, x, k+flk1, 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 bc 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 bac),那么 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{fl11, .2, 3, fl11, 2, 1+fl11, 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 fl11, 2, 1=2, fl11, 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} flk1, x, k f l k − 1 ,   k ,   y fl_{k-1,\ k,\ y} flk1, 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;
}


好了,去吧,皮卡丘!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值