奶牛接力 / Cow Relays G
题目链接:SSL 2510 / luogu P2886 / ybt高效进阶6-1-5
题目大意
给出一张无向连通图,求 S 到 E 经过 k 条边的最短路。
思路
这道题我们可以把矩阵乘法变形一下,就可以 A。
因为每一次如果走到一个点,肯定是从总路径最短的走过来,所以矩阵乘法里面原来是乘的我们就把它改成求那两个值加起来的最小值。
然后赋初值的地方也注意一下,要赋一个很大的值。
然后转移矩阵就是邻接矩阵,然后把转移矩阵乘 k 次,然后就可以得到答案了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll unsigned long long
using namespace std;
struct matrix {
int n, m;
ll a[201][201];
}A, ANS, re;
int n, t, s, e, x[1001], y[1001], num[1001], have[1001];
ll dis[1001], tmp;
matrix operator +(matrix X, matrix Y) {//变形一下
re.n = X.n;
re.m = Y.m;
for (int i = 1; i <= re.n; i++)
for (int j = 1; j <= re.m; j++)
re.a[i][j] = tmp;//因为是最短路,所以要赋值很大
for (int k = 1; k <= X.m; k++)
for (int i = 1; i <= re.n; i++)
for (int j = 1; j <= re.m; j++)
re.a[i][j] = min(re.a[i][j], X.a[i][k] + Y.a[k][j]);
//因为要求最短路,所以变形成这样
return re;
}
void add(int X, int Y, int z) {
A.a[X][Y] = A.a[Y][X] = z;
}
void jzksm(int n) {
ANS = A;
n--;
while (n) {
if (n & 1) ANS = ANS + A;
A = A + A;
n >>= 1;
}
}
int main() {
scanf("%d %d %d %d", &n, &t, &s, &e);
for (int i = 1; i <= t; i++) {
scanf("%lld %d %d", &dis[i], &x[i], &y[i]);
if (!have[x[i]]) {
have[x[i]] = 1;
num[++num[0]] = x[i];
}
if (!have[y[i]]) {
have[y[i]] = 1;
num[++num[0]] = y[i];
}
}
sort(num + 1, num + num[0] + 1);
for (int i = 1; i <= num[0]; i++)
have[num[i]] = i;
A.n = num[0];
A.m = num[0];
memset(A.a, 0x7f, sizeof(A.a));
tmp = A.a[1][1];
for (int i = 1; i <= t; i++) {
add(have[x[i]], have[y[i]], dis[i]);
}
jzksm(n);
printf("%lld", ANS.a[have[s]][have[e]]);
return 0;
}