题目链接

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;
 }