题意:
给定 m m m条无向边,可以随机一个点出发,按照无向边走完所有的城市,到了每个城市之后,可以有两种选择:
- 选择一条与当前城市相连的道路,走向一个没有去过的城市
- 沿着第一次访问该城市时经过的道路后退到上一个城市
当回到起点时,可以选择结束这次旅行或继续旅行。在每到达一个新的城市(包括起点)时,将城市的编号记录下来,这样会形成一个长度为 n n n的序列,请输出字典序最小的序列。
题解:
题目当中的数据只给了
m
=
n
−
1
m=n-1
m=n−1或者
m
=
n
m=n
m=n这两种情况。由于起点可以自由选择,因此我们起点肯定要选择1。我们可以发现,第一种情况其实就是表明这个图没有环。那么对于这种情况,我们只需要对每个城市连接的边的终点城市编号从小到大排序,然后直接
d
f
s
dfs
dfs顺序走完每个节点,在遍历的时候将编号存入
a
n
s
ans
ans数组当中,最后输出即可。
重点是第二种情况。这种情况表明在之前的无环图中加了一条边,由于之前的图其实是一棵树,加了一条边之后,图中一定会形成一个环。例如:
第一种情况的图是:
那么第二种情况图会变成:
对于这种图中只有一个环的图,我们称之为基环图。
我们可以发现,在遍历图的时候,环上总有一条边不会被经过。因此,我们可以枚举环上哪条边不会被经过,然后在 d f s dfs dfs的时候,如果经过这条边直接忽略即可。然后再按照第一种情况的 d f s dfs dfs策略即可找出答案。
实现细节见代码:
#include <bits/stdc++.h>
using namespace std;
int flag, u1[10010], v1[10010], ans[5010], res[5010], all, cnt, du, dv;
bool vis[5010], cir[5010];
vector<int> e[5010];
void dfs1(int from, int pre) { // 找到唯一的环,并存储环上的所有边,方便之后的枚举
vis[from] = 1;
for (int i = 0; i < e[from].size(); i++) {
int to = e[from][i];
if (to == pre) continue;
if (vis[to]) {
flag = 1;
cir[to] = true;
cir[from] = true;
u1[++cnt] = from;
v1[cnt] = to;
return;
}
dfs1(to, from);
if (flag && cir[to]) {
if (cir[from]) {
flag = 0;
u1[++cnt] = from;
v1[cnt] = to;
return;
}
else {
cir[from] = true;
u1[++cnt] = from;
v1[cnt] = to;
return;
}
}
}
}
void dfs2(int from, int pre) {
if (vis[from]) return;
vis[from] = true;
res[++all] = from;
for (int i = 0; i < e[from].size(); i++) {
int v = e[from][i];
if (v == pre) continue;
if ((from == du && v == dv) || (from == dv && v == du)) { // 如果是不被走的边,直接continue
continue;
}
dfs2(v, from);
}
}
void dfs3(int from, int pre) {
if (vis[from]) return;
vis[from] = true;
ans[++all] = from;
for (int i = 0; i < e[from].size(); i++) {
int v = e[from][i];
if (v == pre) continue;
dfs3(v, from);
}
}
int check() { // 保证字典序最小
for (int i = 1; i <= all; i++) {
if (res[i] < ans[i]) {
return 1;
}
else if (res[i] > ans[i]) {
return 0;
}
}
return 0;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
int p = m;
while (p--) {
int u, v;
scanf("%d %d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
sort(e[i].begin(), e[i].end()); // 排序保证字典序最小
}
if (n == m) {
dfs1(1, 0);
int flag = 1;
for (int i = 1; i <= cnt; i++) {
du = u1[i], dv = v1[i];
memset(vis, false, sizeof vis);
all = 0;
dfs2(1, 0);
if (all < n) continue;
if (flag) {
for (int i = 1; i <= all; i++) {
ans[i] = res[i];
}
flag = 0;
}
else {
if (check()) {
for (int i = 1; i <= all; i++) {
ans[i] = res[i];
}
}
}
}
}
else {
dfs3(1, 0);
}
for (int i = 1; i <= n; i++) {
if (i - 1) printf(" ");
printf("%d", ans[i]);
}
return 0;
}