类Floyd算法
类Floyd算法
含义
类Floyd算法故名思意就是和Floyd算法类似的一种最短路算法,可以用于求有负环的图的多源最短路问题
此算法区别于传统Floyd算法的特点——利用两点之间经过边的数量来进行 dp 转移。所以该算法可以解决负环图中的任意两点间最短路问题。理论复杂度 \(O(Nn^3)\) ,倍增优化后复杂度 \(O(log_N n^3)\) 。
首先用动态规划的角度去思考问题,设计状态转移方程
\(dis [k] [i] [j]\) 表示从 \(i\) 到 \(j\) ,恰好经过 \(k\) 条边的最短路径
状态转移方程:
\[d[a+ b , i , j] = min( d[a , i , k ] + d[b , k , j] )
\]
优化思路
仔细观察这个过程不难发现,如果我们知道 \(dis[k][i][x]\) 和 \(dis[k][x][j]\) 就可以得出 \(dis[2k][i][j]\) ,即知道任意 \(dis_{k,i,j}\) 即可求出任意 \(dis_{2k,i,j}\) ,那么同样已知 \(dis_{2k,i,j}\) 即可求出任意 \(dis_{4k,i,j}\) ,诸如此类,利用倍增的思想,我们可以求解出 \(dis_{2^n,i,j}\) 。同样该转移方式满足结合律,利用快速幂可以将复杂度优化到 \(O(log_Nn^3)\) 。
实现方式
实现方式就很简单了,可以类比矩阵乘法的实现方式来实现。(前置知识,矩阵快速幂,矩阵乘法 )
例题 ACwing345.牛站
题目思路很清晰,即求出类最短路算法中 \(dis [N] [i] [j]\) 。
code
#include <bits/stdc++.h>
using namespace std ;
const int maxn = 210 , INF = 0x3f3f3f3f ;
int t , n , m , S , E ;
int g[210][210] , res[210][210] ;
void mul(int c[][maxn] , int a[][maxn] , int b[][maxn])
{
static int T[maxn][maxn] ;
memset(T , 0x3f , sizeof(T)) ;
for(int k = 1 ; k <= n ; k++ )
for(int i = 1 ; i <= n ; i++ )
for(int j = 1 ; j <= n ; j++ )
T[i][j] = min(T[i][j] , a[i][k] + b[k][j]) ;
memcpy(c , T , sizeof(T)) ;
}
void KSM()
{
memset(res , 0x3f , sizeof(res)) ;
for(int i = 1 ; i <= n ; i++) res[i][i] = 0 ;
while( t )
{
if(t & 1 ) mul(res , res , g) ;
mul(g , g , g) ;
t >>= 1 ;
}
}
signed main()
{
cin >> t >> m >> S >> E ;
memset(g , 0x3f , sizeof(g)) ;
map<int , int > q ;
if(! q.count(S)) q[S] = ++n ;
if(! q.count(E)) q[E] = ++n ;
S = q[S] , E = q[E] ;
while( m-- )
{
int u , v , w ;
cin >> w >> u >> v ;
if(! q.count(u)) q[u] = ++n ;
if(! q.count(v)) q[v] = ++n ;
u = q[u] , v = q[v] ;
g[u][v] = g[v][u] = min(g[u][v] , w) ;
}
KSM() ;
cout << res[S][E] ;
return 0 ;
}

浙公网安备 33010602011771号