个人思路:
无环:贪心+dfs
有环:
先tarjan求环
dfs到环上时记住入环点较大的那个同在环上的儿子sec
当到某在环上的点u时,若u点只剩一个同在环上的儿子了,且儿子编号还大于sec,就主动回溯。
(其实是题解(by银河奥赛队员 )的思路,但是代码是我自己的)
蒟蒻调了一天了,把所有题解关于回溯或删边的操作都看遍了,但是自己实现的时候总是wa那么一两个点。
#include<bits/stdc++.h>
using namespace std;
/*
sample in:
10 10
10 7
9 4
10 4
10 3
6 10
9 8
3 8
5 8
2 6
10 1
sample out:
1 10 3 4 9 8 5 6 2 7
*/
const int N = 1e+6 + 5;
int n, m;
struct edge
{
static const int N = 1919810;
int nxt[N], to[N], head[N], pre[N], val[N], cnt;
edge(int c = 0)
{
this -> cnt = 0;
memset(head, 0, sizeof(head));
}
void add(int x, int y, int w = 0)
{
nxt[++cnt] = head[x];
to[cnt] = y;
pre[head[x]] = cnt;
head[x] = cnt;
}
}ed;
int dfn[N], dfncnt, low[N];
int sz, scc[N], in_scc[N];
int s[N], in_stack[N], tp;
void tarjan(int u, int pre)
{
int v;
dfn[u] = low[u] = ++dfncnt;
s[++tp] = u;
in_stack[u] = 1;
for(int i = ed.head[u]; i; i = ed.nxt[i])
{
v = ed.to[i];
if(v == pre) continue;
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if(in_stack[v])
low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
int cnt = 0;
do{
cnt++;
if(cnt >= 2) scc[cnt-1] = v, in_scc[v] = 1;
v = s[tp--];
in_stack[v] = 0;
} while(u != v);
if(cnt >= 2)
{
scc[cnt] = v;
in_scc[v] = 1;
sz = cnt;
}
}
}
bool flag, flag2, flag3;
int sec;
queue<int> q;
bool vis[N];
void DFS(int u)
{
if(!flag2 && !flag3 && in_scc[u]) flag2 = 1;
vis[u] = 1;
q.push(u);
priority_queue<int, vector<int>, greater<int> > pq;
for(int i = ed.head[u]; i; i = ed.nxt[i])
{
if(flag2 && in_scc[ed.to[i]]) sec = max(sec, ed.to[i]);
int v = ed.to[i];
if(!vis[v]) pq.push(v);
}
if(flag2) flag2 = 0, flag3 = 1;
while(!pq.empty())
{
int v = pq.top();
pq.pop();
if(flag && flag3 && in_scc[v])
if(pq.empty() && v > sec)
{
flag = 0;
continue;
}
if(!vis[v]) DFS(v);
}
}
int main()
{
cin >> n >> m;
if(n == m) flag = 1;
for(int i = 1, x, y; i <= m; ++i)
{
cin >> x >> y;
ed.add(x, y);
ed.add(y, x);
}
if(flag) tarjan(1, 0);
// for(int i = 1; i <= sz; ++i)
// cout << scc[i] <<" ";
// cout <<endl;
DFS(1);
while(!q.empty())
{
cout << q.front() <<" ";
q.pop();
}
}