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;
}

浙公网安备 33010602011771号