洛谷 P2886. Cow Relays G
问题描述
分析
题目要求:给定一张
关键点有两个:
- 离散化:虽然顶点编号最大到
,但真正会走到的只有边的端点和 、 ,最多 个。先把它们排序去重、映射到 ,矩阵的规模就从 降到了 ,这是后面矩阵快速幂可行的前提。 - "恰好
条边"不能用普通最短路:Dijkstra/Floyd 求的是"任意条边"的最短路,无法控制恰好 条;且 很大时路径必然绕圈走重复边,必须用能刻画"边数"的状态转移。
思路一:滚动数组 DP
设
初始
思路二:矩阵快速幂(Min-Plus 代数)
把"恰好经过
若
初始矩阵
- 若
的当前最低位为 ,执行 ; - 每次循环后
, 右移一位。
复杂度:每次广义矩阵乘法
正确性:任意一条恰好
参考代码
此代码在AcWing上会超时!洛谷可过!
cpp
#include <bits/stdc++.h>
using namespace std;
int h[210], e[210], ne[210], w[210], tot;
// f[i][j] 表示从 st 出发到达 j 恰好经过 i 条边的最短路
int f[2][210];
int v[210], a[110], b[110], c[110];
int n, t, st, ed, m, p;
void add(int a, int b, int c) {
e[++tot] = b;
w[tot] = c;
ne[tot] = h[a];
h[a] = tot;
}
int main() {
cin >> n >> t >> st >> ed;
v[++m] = st;
v[++m] = ed;
for (int i = 1; i <= t; i++) {
cin >> c[i] >> a[i] >> b[i];
v[++m] = a[i];
v[++m] = b[i];
}
sort(v + 1, v + 1 + m);
p = unique(v + 1, v + 1 + m) - (v + 1);
st = lower_bound(v + 1, v + 1 + p, st) - v;
ed = lower_bound(v + 1, v + 1 + p, ed) - v;
for (int i = 1; i <= t; i++) {
a[i] = lower_bound(v + 1, v + 1 + p, a[i]) - v;
b[i] = lower_bound(v + 1, v + 1 + p, b[i]) - v;
add(a[i], b[i], c[i]);
add(b[i], a[i], c[i]);
}
memset(f, 0x3f, sizeof f);
f[0][st] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= p; j++) {
f[i & 1][j] = 0x3f3f3f3f;
for (int k = h[j]; k; k = ne[k]) {
f[i & 1][j] = min(f[i & 1][j], f[i - 1 & 1][e[k]] + w[k]);
}
}
}
cout << f[n & 1][ed] << endl;
return 0;
}cpp
#include <bits/stdc++.h>
using namespace std;
int v[210], a[110], b[110], c[110];
int d[210][210], ans[210][210];
int n, t, st, ed, m, p;
void mul(int a[][210], int b[][210]) {
int c[210][210];
memset(c, 0x3f, sizeof c);
for (int i = 1; i <= p; i++) {
for (int j = 1; j <= p; j++) {
for (int k = 1; k <= p; k++) {
c[i][j] = min(c[i][j], a[i][k] + b[k][j]);
}
}
}
memcpy(a, c, sizeof c);
}
int main() {
cin >> n >> t >> st >> ed;
v[++m] = st;
v[++m] = ed;
for (int i = 1; i <= t; i++) {
cin >> c[i] >> a[i] >> b[i];
v[++m] = a[i];
v[++m] = b[i];
}
sort(v + 1, v + 1 + m);
p = unique(v + 1, v + 1 + m) - (v + 1);
st = lower_bound(v + 1, v + 1 + p, st) - v;
ed = lower_bound(v + 1, v + 1 + p, ed) - v;
memset(d, 0x3f, sizeof d); // 经过 1 条边的最短路
for (int i = 1; i <= t; i++) {
a[i] = lower_bound(v + 1, v + 1 + p, a[i]) - v;
b[i] = lower_bound(v + 1, v + 1 + p, b[i]) - v;
d[a[i]][b[i]] = min(d[a[i]][b[i]], c[i]);
d[b[i]][a[i]] = min(d[b[i]][a[i]], c[i]);
}
memset(ans, 0x3f, sizeof ans);
ans[st][st] = 0;
while (n) {
if (n & 1) mul(ans, d);
mul(d, d);
n >>= 1;
}
cout << ans[st][ed] << endl;
return 0;
}