D64 最短路 Floyd算法+矩阵快速幂 P2886 [USACO07NOV] Cow Relays G

D64 最短路 Floyd算法+矩阵快速幂 P2886 [USACO07NOV] Cow Relays G_哔哩哔哩_bilibili

 

P2886 [USACO07NOV] Cow Relays G - 洛谷

给定一张 T 条边的无向连通图,求从 S 到 E 经过 n 条边的最短路长度。

思路

因为实际点数只有 200,考虑 Floyd 算法求最短路。

$d[i][j]=d[i][k]+d[k][j]$,这是不限制边数的转移方程。

我们改造一下,$c[i][j]=a[i][k]+b[k][j]$,

表示从 $i$ 到 $j$ 经过 $a+b$ 条边的最短路 $=$ 从 $i$ 到 $k$ 经过 $a$ 条边的最短路 $+$ 从 $k$ 到 $j$ 经过 $b$ 条边的最短路。

 

要走 $n$ 条边,转化为二进制求和,例如 $n=13=(1101)_2=1+4+8$,

则答案数组 $res=d$ 数组走过 $1$ 条边的最短路 $+d$ 数组走过 $4$ 条边的最短路 $+d$ 数组走过 $8$ 条边的最短路。

利用矩阵快速幂的倍增思想,加速求和。

相关板子链接:

D04【模板】最短路 Floyd 算法 - 董晓 - 博客园

G03【模板】矩阵快速幂 - 董晓 - 博客园

 

// 最短路 Floyd算法+矩阵快速幂 O(m^3*logn)=200^3*20
#include<bits/stdc++.h>
using namespace std;

const int N=210;
int n,T,S,E,m;
int t[N][N],d[N][N],res[N][N];

void Floyd(int c[][N],int a[][N],int b[][N]){
  memset(t,0x3f,sizeof t); //初始化临时数组
  for(int k=1; k<=m; k++)
    for(int i=1; i<=m; i++)
      for(int j=1; j<=m; j++)
        t[i][j]=min(t[i][j],a[i][k]+b[k][j]); //数组t=数组a+数组b
  memcpy(c,t,sizeof t); //复制t到c
}
void qsm(){
  memset(res,0x3f,sizeof res);
  for(int i=1;i<=m;i++) res[i][i]=0; //初始化为对角为0的单位矩阵
  while(n){
    if(n&1) Floyd(res,res,d); //res=res+d 累加答案
    Floyd(d,d,d); //d=d+d 即经过2,4,8...条边的最短路
    n>>=1;
  }
}
int main(){
  cin>>n>>T>>S>>E; //n条边,T条边,起点,终点
  memset(d,0x3f,sizeof d); //注意d[i][i]不能初始化为0,因自己走向自己不合法
  map<int,int> mp;
  if(!mp.count(S)) S=(mp[S]=++m); //点的离散化,把大整数映射为小整数
  if(!mp.count(E)) E=(mp[E]=++m);
  for(int a,b,c;T--;){
    cin>>c>>a>>b;
    if(!mp.count(a)) mp[a]=++m;
    if(!mp.count(b)) mp[b]=++m; //最多边数T=100,点数m=200
    a=mp[a]; b=mp[b];
    d[a][b]=d[b][a]=min(d[a][b],c); //初始时d为仅经过一条边的最短路
  }
  
  qsm(); //矩阵快速幂
  cout<<res[S][E];
}

 

posted @ 2026-02-11 22:35  董晓  阅读(157)  评论(0)    收藏  举报