「NOIP 2018」 旅行
给出一个 \(n\) 个点, \(m\) 条边的无向联通图,可以从任意一个点出发,前往一个相邻未访问过的结点,或者沿第一次访问当前结点的边返回。要求找出一个访问方案,使得每个结点都被访问过,且方案的字典序最小。(\(m=n-1\text{ 或 }m=n\))
分析
当 \(m=n-1\) 时,显然是一棵树,很容易想到用贪心,从 \(1\) 号结点开始访问,每次去往一个未访问过的数字最小的结点。所以我们直接将每个点的相邻结点排序,然后暴搜就好了。
当 \(m=n\) 时,图是一棵基环树。这时用之前的策略,很容易找到反例。于是我们手推样例,发现在环上一定有一条边是不会被经过的,所以我们可以找到这条边,将其删去,就可以继续按照 \(m=n-1\) 的做法做了。
具体如下:
首先找环。假设当前结点为 \(u\) ,它的一个相邻结点为 \(v\) ,如果 \(v\) 已经被访问过了,而且还没有找到一个在环上的结点,那么 \(u\) 和 \(v\) 一定在环上,这时,我们将 \(v\) 标记为第一个环上的结点。而后回溯时,如果子节点是环上的点,那么当前结点也会是环上的点,但 \(v\) 的父亲结点除外。
找到所有的环上的点后,我们再找到环上的边,把它们储存下来。然后暴力枚举删去哪条边,按照 \(m=n-1\) 的做法求出方案,然后比较,取字典序最小的方案。
这个方法可谓是相当的暴力......事实证明,它跑的确实也很慢.......
代码
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 5001
#define il inline
#define re register
#define INF 0x3f3f3f3f
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;
template <typename T> inline void read(T &x) {
T f = 1; x = 0; char c;
for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
x *= f;
}
struct edge {
int to, nxt;
} e[N<<1];
struct huan {
int u, v;
} h[N];
int n, m, tot, c, lst;
int head[N<<1],cnt;
int ans[N], ret[N];
bool inh[N], vis[N];
vector <int> g[N];
void insert(int u, int v) { e[++cnt].to = v, e[cnt].nxt = head[u], head[u] = cnt; }
void dfs1(int u, int fa) {
ans[++c] = u; vis[u] = 1;
for (int i = 0; i < g[u].size(); ++i) {
int v = g[u][i];
if (!vis[v]) dfs1(v, u);
}
}
void find(int u, int fa) {
vis[u] = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v == fa) continue;
if (vis[v] && lst) continue;
if (vis[v] && !lst) { inh[u] = 1, lst = v; continue; }
find(v, u);
if (inh[v] && v != lst) inh[u] = 1;
}
}
void add(int u, int fa) {
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v == fa) continue;
if (inh[v]) {
h[++tot] = (huan){u, v};
if (v == lst) return;
add(v, u);
return;
}
}
}
void dfs2(int u, int fa, int k) {
ret[++c] = u, vis[u] = 1;
int a = h[k].u, b = h[k].v;
for (int i = 0; i < g[u].size(); ++i) {
int v = g[u][i];
if (!vis[v]) {
if ((u == a && v == b) || (u == b && v == a)) continue;
dfs2(v, u, k);
}
}
}
void cmp() {
for (int i = 1; i <= n; ++i)
if (ans[i] > ret[i]) {
memcpy(ans, ret, sizeof ret);
break;
}
else if (ans[i] < ret[i]) break;
}
int main() {
int u, v;
read(n), read(m);
for (int i = 1; i <= m; ++i) {
read(u), read(v);
insert(u, v), insert(v, u);
g[u].push_back(v), g[v].push_back(u);
}
for (int i = 1; i <= n; ++i) sort(g[i].begin(), g[i].end());
if (m == n - 1) dfs1(1, 0);
else {
find(1, 0);
add(lst, 0);
memset(ans, INF, sizeof ans);
for (int i = 1; i <= tot; ++i) {
memset(vis, 0, sizeof vis);
c = 0;
dfs2(1, 0, i);
cmp();
}
}
for (int i = 1; i <= n; ++i) printf("%d ", ans[i]);
return 0;
}

浙公网安备 33010602011771号