floyd矩阵快速幂求经过N条边的最短路
矩阵结论: 假设一个邻接矩阵A中,有A[i][j] = 1表示i与j有边,否则无边,那么B = A$\times$A中,所有B[i][j] = 1都是经过2条边且仅经过2条边可达的两个点。同理B = $A^N$中,所有B[i][j] = 1表示的是从i到j经过N条边且仅经过N条边是可达的。从两个矩阵相乘的规则来看(A的行 \(\times\) B的列),相乘的情况只可能是 A[i][k]\(\times\)B[k][j] 这种情况。
只有当A[i][k] = A[k][j] = 1的时候,A[i][k]\(\times\)A[k][j] 才能为1。所以A\(\times\)A所得出的所有为1的情况都是两点经过且仅经过2条边可达。
floyd结合上面的结论能求出经过N条边的最短路。
传统的floyd是在自身矩阵上做松弛操作的,每次选取一个k点,去松弛所有的边,因为是在自身矩阵上做松弛,所以矩阵是在变化的,也就导致了自身矩阵在不断的做松弛,这样求出来的最短路是经过若干条边的。
要求固定边数的最短路应该要用另外一个矩阵临时存储,然后迭代跑floyd。
假设求邻接矩阵A经过2条边的最短路(经过1条边,其实就是\(A^1\)),B = floyd(A, A),因为A不会变,所以每两个点就只能通过两条边松弛。同理 C = floyd(B, B),C就是经过4条边的最短路矩阵(\(A^4\))。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MaxnN = 200+10;
const int MaxnM = 2e5+10;
const int INF = 0x3f3f3f3f;
const ll LINF = 1e18;
int T, N, S, E, cnt;
int Hash[MaxnN<<1];
pair<int, pair<int, int> > p[MaxnN];
struct Matrix {
int a[MaxnN][MaxnN];
Matrix operator * (const Matrix &x) const {
Matrix tmp;
memset(tmp.a, INF, sizeof(tmp.a)); // 可能会有自环,所以全部初始化为 INF
for(int k = 0; k < cnt; ++k) { // floyd
for(int i = 0; i < cnt; ++i) {
for(int j = 0; j < cnt; ++j) {
tmp.a[i][j] = min(tmp.a[i][j], a[i][k]+x.a[k][j]);
}
}
}
return tmp;
}
} ans, a;
void solve() {
N--;
ans = a;
while(N) {
if(N&1) ans = ans*a;
N >>= 1;
a = a*a;
}
}
int main(void)
{
scanf("%d%d%d%d", &N, &T, &S, &E);
int u, v, w;
cnt = 0;
for(int i = 0; i < T; ++i) {
scanf("%d%d%d", &w, &u, &v);
p[i].first = u; p[i].second.first = v;
p[i].second.second = w;
Hash[cnt++] = u;
Hash[cnt++] = v;
}
sort(Hash, Hash+cnt);
cnt = unique(Hash, Hash+cnt)-Hash;
memset(a.a, INF, sizeof(a.a));
for(int i = 0; i < T; ++i) {
u = p[i].first; v = p[i].second.first;
w = p[i].second.second;
u = lower_bound(Hash, Hash+cnt, u)-Hash;
v = lower_bound(Hash, Hash+cnt, v)-Hash;
a.a[u][v] = a.a[v][u] = min(a.a[u][v], w);
}
solve();
S = lower_bound(Hash, Hash+cnt, S)-Hash;
E = lower_bound(Hash, Hash+cnt, E)-Hash;
printf("%d\n", ans.a[S][E]);
return 0;
}
浙公网安备 33010602011771号