思路
根据题目所给的数据范围,考虑可能是 O ( n 2 ) O(n^2) O(n2)的复杂度的算法,
- n = m + 1 n=m+1 n=m+1的时候,是一棵树,很明显就是选择子节点中的小的那个点先遍历。
- n = m n=m n=m的时候,基环树,我们可以发现如果要遍历图中的所有顶点,那么必然有一条边是不会遍历到的,而这条边的选择,就会影响到结果序列中字典序的大小,也就是说你选择一条边不走,一旦到达这条边就跳过,那么在剩下的树中遍历的时候,结果是不同的,我们就暴力的去枚举这条边就好了。
注意建图的时候借助到了邻接矩阵,根据邻接矩阵在用前向星建图,使得每个节点的子节点都是按照从小到大的顺序排列的。
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 5050;
int n, m;
int grap[maxn][maxn], res[maxn];
struct edge {
int from, to,nxt;
} edges[maxn<<1];
int tot, head[maxn];
void add_edge(int u, int v) {
edges[tot] = edge{u, v, head[u]};
head[u] = tot++;
}
int vis[maxn], ans[maxn], cnt1;
void dfs1(int now) {
vis[now] = 1;
ans[cnt1++] = now;
for (int i = head[now]; ~i; i = edges[i].nxt) {
int to = edges[i].to;
if (vis[to]) continue;
dfs1(to);
}
}
int uu[maxn], vv[maxn], cnt2, flag, entrance;
// uu[i]-vv[i]环种第i条边的两个端点。
void get_loop(int now, int fa) {
vis[now] = 1;
for (int i = head[now]; ~i; i = edges[i].nxt) {
int to = edges[i].to;
if (to == fa) continue;
if (!flag and vis[to]) { // 发现环的起点
flag=1;
entrance = to; // 环的入口
uu[cnt2]=now; vv[cnt2]=fa; cnt2++;
uu[cnt2]=to; vv[cnt2]=now; cnt2++;
return ;
}
get_loop(to, now);
if (flag) { //说明此点now在环上。
if (now != entrance) { //到达entrance的时候就是出环的时候
uu[cnt2]=now; vv[cnt2]=fa; cnt2++;
}
else flag = 0;
return ; //找到了环就不需要再找了
}
}
}
int tmp[maxn];
void dfs2(int now) {
vis[now] = 1;
tmp[cnt1++] = now;
for (int i = head[now]; ~i; i = edges[i].nxt) {
int to = edges[i].to;
if (vis[to] or !grap[now][to]) continue;
dfs2(to);
}
}
bool check() {
for (int i = 0; i < n; i++) {
if (ans[i] < tmp[i]) return 0;
else if (ans[i] > tmp[i]) return 1;
}
return 0;
}
void update() {
for (int i = 0; i < n; i++)
ans[i] = tmp[i];
}
int main(int argc, char const *argv[])
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
memset(head, -1, sizeof head);
for (int i=1; i<=m; i++) {
int u, v;
cin >> u >> v;
grap[u][v] = grap[v][u] = 1;
}
for (int u = 1; u <= n; u++) {
for (int v = n; v >= 1; v--) {
if (grap[u][v]) add_edge(u, v); // 按照从小到大的顺序建立邻接表(用的是前向星)
}
}
if (m == n-1) dfs1(1);
else {
get_loop(1, 0);
memset(ans, 0x3f, sizeof ans);
int cir_num = cnt2; // 环中边的数量
for (int i = 0; i < cir_num; i++) {
memset(vis, 0, sizeof vis);
int delu = uu[i], delv = vv[i]; // 枚举要删除的两个环中的点
grap[delu][delv] = grap[delv][delu] = 0;
vis[1] = 1; cnt1 = 0;
dfs2(1);
grap[delu][delv] = grap[delv][delu] = 1;
if (check()) update(); // 找到字典序最小的答案
}
}
for (int i = 0; i < n; i++)
cout << ans[i] << " ";
return 0;
}