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;
}
posted @ 2024-10-04 14:10  Unino  阅读(35)  评论(0)    收藏  举报