作物杂交 SPFA -- dp
题目
思路
首先回想一下bellman-ford算法通过\(n - 1\)次枚举,每次枚举所有的边进行松弛,算法的复杂度为\(O(n \times m)\), 我们使用队列进行优化,即每次可能松弛其他点的边一定是上一次被松弛的点, 所以我们可以用一个队列来维护当前点是否有可能松弛其他的边这也就是SPFA。
现在这道题之规定了\(<a, b, c>, <b, a, c>\), 即通过\(a, b\)到达\(c\), 或\(b, a\), 边权取\(max(t[a], t[b])\), 这里难在如何建图上, 一个傻瓜的方法是建立上述四条边,然后跑最短路, 但是源点我们并不确定,如果跑Floyd很容易很容易超时。
我们可以考虑dp, \(f[i][j]\) 表示考虑前i个点,生成\(j\)所需的最小时间, 这一步可以从\(max(f[i - 1][a], f[i - 1][b]) + max(w[a], w[b])\), \(a, b\)是所有生成\(b\)的组合, 但是\(O(n\times m)\)的复杂度显然是不够的, 我们可以考虑用队列在进行优化,因为\(f[i][j]\) 只能被已经产生的点, 即合成的种子中去转移,所以我们用队列维护已经产生的点的,这样我们可以把复杂度降低到平均\(O(m)\),当然最坏的情况可能是\(O(n \times m)\)
Code
#include <iostream>
#include <queue>
#include <cstring>
using i64 = long long;
const int N = 2e3 + 10, M = 2e5 + 10;
int n, m, k, t, w[N], dist[N];
int h[N], e[M], ne[M], target[M], idx;
int f[N][N];
std::queue<int> q1;
bool st[N];
void add(int a, int b, int c) {
ne[idx] = h[a], h[a] = idx, target[idx] = c, e[idx ++] = b;
}
void spfa() {
while (q1.size()) {
int x = q1.front(); q1.pop();
st[x] = false;
for (int i = h[x]; ~i; i = ne[i]) {
int y = e[i], z = target[i];
if (dist[z] > std::max(dist[x], dist[y]) + std::max(w[x], w[y])) {
dist[z] = std::max(dist[x], dist[y]) + std::max(w[x], w[y]);
if (!st[z]) {
st[z] = true;
q1.push(z);
}
}
}
}
}
int main() {
memset(dist, 0x3f, sizeof dist);
memset(h, -1, sizeof h);
std::cin >> n >> m >> k >> t;
for (int i = 1; i <= n; i ++) {
std::cin >> w[i];
}
for (int i = 1; i <= m; i ++) {
int x;
std::cin >> x;
dist[x] = 0;
q1.push(x);
st[x] = true;
}
for (int i = 1; i <= k; i ++) {
int a, b, c;
std::cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
spfa();
std::cout << dist[t];
}