Floyd + 矩阵快速幂 + 离散化 - Cow Relays - POJ 3613

Floyd + 矩阵快速幂 + 离散化 - Cow Relays - POJ 3613

题意:

给定一张由T条边构成的无向图,点的编号为1~1000之间的整数。

求从起点S到终点E恰好经过N条边(可以重复经过)的最短路。

注意: 数据保证一定有解。

输入格式

第1行:包含四个整数N,T,S,E。

第2…T+1行:每行包含三个整数,描述一条边的边长以及构成边的两个点的编号。

输出格式

输出一个整数,表示最短路的长度。

数据范围

2 ≤ T ≤ 100 , 2 ≤ N ≤ 1 0 6 2≤T≤100, 2≤N≤10^6 2T100,2N106

输入样例:

2 6 6 4
11 4 6
4 4 8
8 4 9
6 6 8
2 6 9
3 8 9

输出样例:

10

分析:

我 们 改 变 一 下 f l o y d 算 法 中 的 d 数 组 的 含 义 : 我们改变一下floyd算法中的d数组的含义: floydd

d [ k ] [ i ] [ j ] : 表 示 从 i 到 j , 且 经 过 恰 好 k 条 边 的 最 短 距 离 。 d[k][i][j]:表示从i到j,且经过恰好k条边的最短距离。 d[k][i][j]ijk

假 设 k = a + b , 即 k 条 边 分 为 两 部 分 , 前 一 部 分 为 a 条 边 , 后 一 部 分 为 b 条 边 , 第 a + 1 个 点 设 为 点 s 。 假设k=a+b,即k条边分为两部分,前一部分为a条边,后一部分为b条边,第a+1个点设为点s。 k=a+bkaba+1s

则 d [ k ] [ i ] [ j ] = m i n ( d [ k ] [ i ] [ j ] , d [ a ] [ i ] [ s ] + d [ b ] [ s ] [ j ] ) 。 则d[k][i][j]=min(d[k][i][j],d[a][i][s]+d[b][s][j])。 d[k][i][j]=min(d[k][i][j],d[a][i][s]+d[b][s][j])

若 我 们 每 次 增 加 1 条 边 计 算 最 短 路 径 , 最 多 1 0 6 条 边 , 每 次 f l o y d 要 O ( n 3 ) , 会 超 时 。 若我们每次增加1条边计算最短路径,最多10^6条边,每次floyd要O(n^3),会超时。 1106floydO(n3)

但 是 由 于 最 短 路 的 性 质 : 一 分 为 二 , 两 相 互 独 立 , 分 别 计 算 最 短 路 再 相 加 。 但是由于最短路的性质:一分为二,两相互独立,分别计算最短路再相加。

故 我 们 可 以 类 比 到 矩 阵 乘 法 中 倍 增 的 思 想 , 利 用 矩 阵 快 速 幂 来 计 算 l o g 2 m 次 即 可 。 故我们可以类比到矩阵乘法中倍增的思想,利用矩阵快速幂来计算log_2m次即可。 log2m

时 间 复 杂 度 降 到 O ( n 3 l o g 2 m ) 。 时间复杂度降到O(n^3log_2m)。 O(n3log2m)

这 就 告 诉 我 们 , f l o y d 算 法 可 以 扩 展 求 经 过 恰 好 k 条 边 的 最 短 路 , 时 间 复 杂 度 是 O ( n 3 l o g 2 m ) 。 这就告诉我们,floyd算法可以扩展求经过恰好k条边的最短路,时间复杂度是O(n^3log_2m)。 floydkO(n3log2m)

问题:如何逐次增加边的数量来更新最短路?

我 们 用 二 维 数 组 r e s 来 保 存 经 过 恰 好 k 条 边 的 任 意 两 点 间 的 最 短 距 离 。 我们用二维数组res来保存经过恰好k条边的任意两点间的最短距离。 resk

初 始 化 r e s 为 i n f , r e s [ i ] [ i ] = 0 , 表 示 恰 好 经 过 0 条 边 的 任 意 两 点 间 的 最 短 距 离 。 初始化res为inf,res[i][i]=0,表示恰好经过0条边的任意两点间的最短距离。 resinfres[i][i]=00

接 着 我 们 用 输 入 的 邻 接 矩 阵 更 新 r e s 数 组 , 借 鉴 快 速 幂 倍 增 的 思 想 来 更 新 k 次 即 可 。 接着我们用输入的邻接矩阵更新res数组,借鉴快速幂倍增的思想来更新k次即可。 resk

注意:

初 始 化 邻 接 矩 阵 g 时 , 不 能 初 始 化 g [ i ] [ i ] = 0 , 因 为 这 表 示 存 在 边 权 为 0 的 自 环 , 事 实 上 是 没 有 的 。 初始化邻接矩阵g时,不能初始化g[i][i]=0,因为这表示存在边权为0的自环,事实上是没有的。 gg[i][i]=00

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>

using namespace std;

const int N=110;

int n,m,k,S,E;
int g[N][N];
int res[N][N];
map<int,int> M;

void mul(int a[][N],int b[][N])
{
    int c[N][N];
    memset(c,0x3f,sizeof c);
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                c[i][j]=min(c[i][j],a[i][k]+b[k][j]);
    memcpy(a,c,sizeof c);
}

void quick_pow()
{
    memset(res,0x3f,sizeof res);
    for(int i=1;i<=n;i++) res[i][i]=0;  //初始经过0条边
    
    while(k)
    {
        if(k&1) mul(res,g);
        mul(g,g);
        k>>=1;
    }
}

int main()
{
    cin>>k>>m>>S>>E;
    
    M[S]=++n,M[E]=++n;
    S=M[S],E=M[E];
    
    memset(g,0x3f,sizeof g);  //g的含义是经过一条边的距离,故不能初始化g[i][i]=0,因为不存在权为0的自环
    int a,b,c;
    while(m--)
    {
        cin>>c>>a>>b;
        if(!M.count(a)) M[a]=++n;
        if(!M.count(b)) M[b]=++n;
        a=M[a],b=M[b];
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    
    quick_pow();
    
    cout<<res[S][E]<<endl;
    
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值