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 算法 - 董晓 - 博客园
// 最短路 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]; }
浙公网安备 33010602011771号