CSP-S 2022 假期计划
BFS,贪心,思维
算法 1
跑一遍 Floyd,然后枚举所有可能 \((A, B, C, D)\)。时间复杂度为 \(O(n^4)\)。
算法 2
对于 \(k = 0\),枚举 \(1\) 的出边,得到 \((A, D)\),然后再枚举 \((B, C)\)。时间复杂度最坏为 \(O(n)\)。
算法 3
路线为 \(1 \to A \to B \to C \to D \to 1\),如果拆开可以得到 \(1 \to A \to B\) 和 \(C \to D \to 1\),对于每条路径,如果两个端点确定,则可以优化求中间点。于是我们枚举满足要求的 \((B, C)\),即换乘次数不超过 \(k\) 次。此时两条路径的两段确定,那么我们肯定希望中间点的分数最大,于是我们可以预处理出 \(v = f(u, 0/1/2)\) 表示 \((1, v)\) 和 \((u, v)\) 的换乘次数不超过 \(k\) 次且分数最大、次大、第三大。为什么要预处理出三个呢?因为可能会与其它的两个点重复,例如 \(A = f(B)\),则 \(A\) 可能与 \(C\)、\(D\) 重复。预处理可以用 BFS,因为边权都是 \(1\)。时间复杂度只有 \(O(n)\) 小于 Dijkstra 的 \(O(m \log n)\)。
时间复杂度为 \(O(n^2)\)。
#include <bits/stdc++.h>
typedef long long LL;
const int N = 2505;
int n, m, k;
LL s[N];
std::vector<int> g[N];
int dis[N][N], f[N][3];
void bfs(int x) {
for (int i = 1; i <= n; i++)
dis[x][i] = -1;
std::queue<int> q;
q.push(x);
dis[x][x] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto v : g[u]) {
if (dis[x][v] == -1) {
dis[x][v] = dis[x][u] + 1;
q.push(v);
}
}
}
for (int i = 1; i <= n; i++) {
if (dis[x][i] > k)
dis[x][i] = -1;
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m >> k;
k++;
for (int i = 2; i <= n; i++) {
std::cin >> s[i];
}
for (int i = 1; i <= m; i++) {
int u, v;
std::cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
bfs(1);
for (int i = 2; i <= n; i++) {
bfs(i);
for (int j = 2; j <= n; j++) {
if (i == j) continue;
if (dis[1][j] == -1 || dis[i][j] == -1) continue;
if (s[j] > s[f[i][0]]) {
f[i][2] = f[i][1];
f[i][1] = f[i][0];
f[i][0] = j;
} else if (s[j] > s[f[i][1]]) {
f[i][2] = f[i][1];
f[i][1] = j;
} else if (s[j] > s[f[i][2]]) {
f[i][2] = j;
}
}
}
LL ans = 0;
for (int b = 2; b <= n; b++) {
for (int c = b + 1; c <= n; c++) {
if (dis[b][c] == -1) continue;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
int a = f[b][i], d = f[c][j];
if (!a || !d || a == c || d == b || a == d) continue;
ans = std::max(ans, s[a] + s[b] + s[c] + s[d]);
}
}
}
}
std::cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号