NOIP2018 提高组 旅行

贪心,搜索,基环树

算法 1

对于 \(m=n-1\)

也就是树的情况,当我们选择了节点 \(u\) 时,就必须往下一直走完。显然我们从 \(1\) 开始,字典序最小。然后将每个点的出边排序,我们每次一定会选择一个最小的走。时间复杂度也就是 \(O(n \log n)\)

算法 2

对于 \(100\%\) 的数据。

此时 \(m=n\)。在树的情况下多了一条边,说明有一个环。因为我们不可以往下走到一个已经走过的点,所以还会走 \(n-1\) 条边,因此我们可以枚举断边,找出字典序最小的那个即可。时间复杂度为 \(O(n^2 \log n)\)。(这份代码枚举了删除所有的边)

#include <bits/stdc++.h>

const int N = 5005;

int n, m, cnt;
std::vector<std::pair<int, int>> edges;
std::vector<int> g[N], res, ans;
bool vis[N];

void dfs(int u) {
  cnt++;
  res.push_back(u);
  for (auto v : g[u]) {
    if (vis[v]) {
      continue;
    }
    vis[v] = true;
    dfs(v);
  }
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int u, v;
    std::cin >> u >> v;
    edges.push_back({u, v});
    g[u].push_back(v);
    g[v].push_back(u);
  }
  for (int i = 1; i <= n; i++) {
    std::sort(g[i].begin(), g[i].end());
  }
  if (m == n - 1) {
    vis[1] = true;
    dfs(1);
    for (int i = 0; i < n; i++) {
      std::cout << res[i] << " \n"[i == n - 1];
    }
    return 0;
  }
  for (int i = 0; i < m; i++) {
    cnt = 0;
    res.clear();
    for (int j = 1; j <= n; j++) {
      vis[j] = false;
    }
    int u, v;
    if (i > 0) {
      std::tie(u, v) = edges[i - 1];
      g[u].push_back(v);
      g[v].push_back(u);
      std::sort(g[u].begin(), g[u].end());
      std::sort(g[v].begin(), g[v].end());
    }
    std::tie(u, v) = edges[i];
    auto it = std::lower_bound(g[u].begin(), g[u].end(), v);
    g[u].erase(it, it + 1);
    it = std::lower_bound(g[v].begin(), g[v].end(), u);
    g[v].erase(it, it + 1);
    vis[1] = true;
    dfs(1);
    if (cnt == n && (ans.empty() || res < ans)) {
      ans = res;
    }
  }
  for (int i = 0; i < n; i++) {
    std::cout << ans[i] << " \n"[i == n - 1];
  }
  return 0;
}
posted @ 2024-09-01 21:12  Unino  阅读(11)  评论(0)    收藏  举报