题目链接:https://www.luogu.com.cn/problem/P5049
题目描述
小 Y 是一个爱好旅行的 OIer。她来到 X 国,打算将各个城市都玩一遍。
小Y了解到, X国的 n 个城市之间有 m 条双向道路。每条双向道路连接两个城市。 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且, 从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些 道路从一个城市前往另一个城市。
小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。
为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 nn 的序列。她希望这个序列的字典序 最小,你能帮帮她吗? 对于两个长度均为 nn 的序列 AA 和 BB,当且仅当存在一个正整数 xx,满足以下条件时, 我们说序列 AA 的字典序小于 BB。
对于任意正整数 1 ≤ i < x1≤i<x,序列 A 的第 i 个元素Ai 和序列 B 的第 i 个元素 Bi相同。
序列 A 的第 x 个元素的值小于序列 B 的第 x 个元素的值。
输入格式
输文件共 m+1 行。第一行包含两个整数 n,m(m≤n),中间用一个空格分隔。
接下来 m 行,每行包含两个整数 u,v(1≤u,v≤n) ,表示编号为 u 和 v 的城市之 间有一条道路,两个整数之间用一个空格分隔。
输出格式
输出文件包含一行,nn 个整数,表示字典序最小的序列。相邻两个整数之间用一个 空格分隔。
输入
6 5 1 3 2 3 2 5 3 4 4 6
输出
1 3 2 5 4 6
输入
6 6 1 3 2 3 2 5 3 4 4 5 4 6
输出:
1 3 2 4 5 6
该题m有两种情况,对于m = n - 1的情况,这是一颗树,我们只需要从1开始,往后遍历时,每次选择所连的点(未被访问过)中,编号最小的那个(贪心)
对于m = n的情况下,图中绝对会有一个环,这种图叫做基环树,
遇见基环树,首要的任务是找环 :
① 在学习Tarjan算法的时候,我们可以知道改环中任意两点都是可以相互到达的(SCC),所以说我们可以用Tarjan算法来找出环 最稳妥
② 可以用 dfs 来模拟Tarjan算法的过程来找环
以下为本人两种常用的dfs找环的代码,均可通过此题
bool vis[500007]; int in[500007],tot,rt; int getLoop(int u,int fa) { if(vis[u]) { rt = u; return 1; } int tmp; vis[u] = true; for(int i=head[u]; ~i; i=e[i].nex) { int to = e[i].v; if(to == fa) continue; tmp = getLoop(to,u); if(tmp) { if(tmp == 1) { in[u] = 1; if(u != rt) return 1; } return 2; } } return 0; }
int getLoop2(int u,int fa) { vis[u] = true; for(int i=head[u]; ~i; i=e[i].nex) { int to = e[i].v; if(to == fa) continue; if(vis[to]) { in[u] = 1; return to; } else { int ret = getLoop2(to,u); if(ret == 0) return 0; if(ret == -1) continue; in[u] = 1; if(ret == u) return 0; else return ret; } } return -1; }
对于基环树的情况,大致有两种复杂度的情况{
1. 找到环,然后枚举环上的边,断掉一条边之后,便到了树的情况
2. 因为基环树比树多了一条边,所以在 dfs找答案的时候就可以选择一条边不走。按照贪心策略在dfs过程中,考虑在什么情况下会选择舍弃一条边?{
① 毋庸置疑,首先我们舍弃的这一条边是在环上的
② 舍弃了这条边之后,就立即进行回溯,因为舍弃的边连接的是当前点的最大儿子节点
③ 我们在回溯之后,再往后走的那个点的编号一定是比舍弃的那条边连接的点要小
}
}
以上两种找环的方式时间复杂度相差不大(几乎没有差距)
int cnt,head[500007],n,m,flag; vector<int> ans; struct node { int v,nex,u; } e[maxn]; void add(int u,int v) { e[cnt].u = u; e[cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt ++; } void init() { cnt = 0; for(int i=0; i <= 500000; i++) head[i] = -1; } bool vis[500007]; int in[500007],tot,rt; int getLoop(int u,int fa) { if(vis[u]) { rt = u; return 1; } int tmp; vis[u] = true; for(int i=head[u]; ~i; i=e[i].nex) { int to = e[i].v; if(to == fa) continue; tmp = getLoop(to,u); if(tmp) { if(tmp == 1) { in[u] = 1; if(u != rt) return 1; } return 2; } } return 0; } int getLoop2(int u,int fa) {/// pre_version vis[u] = true; for(int i=head[u]; ~i; i=e[i].nex) { int to = e[i].v; if(to == fa) continue; if(vis[to]) { in[u] = 1; return to; } else { int ret = getLoop2(to,u); if(ret == 0) return 0; if(ret == -1) continue; in[u] = 1; if(ret == u) return 0; else return ret; } } return -1; } void dfs(int u,int fa) { ans.push_back(u); vis[u] = true; vector<int> vet; for(int i=head[u]; ~i; i=e[i].nex) { int to = e[i].v; if(vis[to]) continue; vet.push_back(to); } sort(vet.begin(),vet.end()); int siz = vet.size(); for(int i=0; i<siz; i++) { int to = vet[i]; if(vis[to]) continue; if(flag && i == siz - 1 && in[u] && in[to] && to > fa) { flag = 0; continue; } if(i + 1 < siz) dfs(to,vet[i+1]); else dfs(to,fa); } } int main() { n = read,m = read; init(); for(int i=1; i<=m; i++) { int u = read,v = read; add(u,v),add(v,u); } memset(vis,0,sizeof vis); if(n == m) getLoop(1,0),flag = 1;/// ac 2 3 4 5 memset(vis,0,sizeof vis); dfs(1,n+1); for(int i=1; i<=n; i++) { printf("%d%c",ans[i-1],i<n?' ':'\n'); } return 0; } /** 6 6 1 3 2 3 2 5 3 4 4 5 4 6 **/
Tarjan做法:
时间相差不大,可能多了个 stl 的常数吧
int cnt,head[500007],n,m,flag; vector<int> ans; struct node { int v,nex,u; } e[maxn]; void add(int u,int v) { e[cnt].u = u; e[cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt ++; } void init() { cnt = 0; for(int i=0; i <= 500000; i++) head[i] = -1; } bool vis[500007]; int in[500007],tot,tim; int dfn[500007],low[500007];///,pos[500007]; stack<int> st;///Tarjan use void Tarjan(int x,int fa) { dfn[x] = low[x] = ++ tim; st.push(x); vis[x] = 1; for(int i=head[x]; ~i; i=e[i].nex) { int to = e[i].v; if(to == fa) continue; if(!dfn[to]) { Tarjan(to,x); low[x] = min(low[x],low[to]); } else if(vis[to] == 1) { low[x] = min(low[x],dfn[to]); } } if(low[x] == dfn[x]) { vector<int> path; path.push_back(x); while(st.top() != x) { path.push_back(st.top()); vis[st.top()] = 0; st.pop(); } st.pop(); if(path.size() >= 2) { for(int i=0; i<path.size(); i++) in[path[i]] = 1; return ; } } } void dfs(int u,int fa) { ans.push_back(u); vis[u] = true; vector<int> vet; for(int i=head[u]; ~i; i=e[i].nex) { int to = e[i].v; if(vis[to]) continue; vet.push_back(to); } sort(vet.begin(),vet.end()); int siz = vet.size(); for(int i=0; i<siz; i++) { int to = vet[i]; if(vis[to]) continue; if(flag && i == siz - 1 && in[u] && in[to] && to > fa) { flag = 0; continue; } if(i + 1 < siz) dfs(to,vet[i+1]); else dfs(to,fa); } } int main() { n = read,m = read; init(); for(int i=1; i<=m; i++) { int u = read,v = read; add(u,v),add(v,u); } memset(vis,0,sizeof vis); if(n == m) Tarjan(1,0),flag = 1;/// ac 2 3 4 5 memset(vis,0,sizeof vis); dfs(1,n+1); for(int i=1; i<=n; i++) { printf("%d%c",ans[i-1],i<n?' ':'\n'); } return 0; } /** 6 6 1 3 2 3 2 5 3 4 4 5 4 6 **/
本题在找环的过程中,像极了2017年蓝桥杯决赛的一道题 发现环
其实这个题再想一下的话,就可以发现,可以用拓扑排序做出来
方法大同小异咯
干脆也写了一下,拓扑排序写法:
#define Clear(x,val) memset(x,val,sizeof x) int cnt,head[500007],n,m,flag; vector<int> ans; struct node { int v,nex,u; } e[maxn]; void add(int u,int v) { e[cnt].u = u; e[cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt ++; } void init() { cnt = 0; for(int i=0; i <= 500000; i++) head[i] = -1; } bool vis[500007]; int in[500007],tot,tim; int dfn[500007],low[500007];///,pos[500007]; stack<int> st;///Tarjan use int deg[500007]; void topoSort() { queue<int> que; for(int i=1; i<=n; i++) { if(deg[i] == 1) que.push(i),vis[i] = 1; } while(que.size()) { int frt = que.front(); que.pop(); if(deg[frt] == 1) in[frt] = 0; for(int i=head[frt]; ~i; i=e[i].nex) { int to = e[i].v; deg[to] --; if(deg[to] == 1) que.push(to); } } } void dfs(int u,int fa) { ans.push_back(u); vis[u] = true; vector<int> vet; for(int i=head[u]; ~i; i=e[i].nex) { int to = e[i].v; if(vis[to]) continue; vet.push_back(to); } sort(vet.begin(),vet.end()); int siz = vet.size(); for(int i=0; i<siz; i++) { int to = vet[i]; if(vis[to]) continue; if(flag && i == siz - 1 && in[u] && in[to] && to > fa) { flag = 0; continue; } if(i + 1 < siz) dfs(to,vet[i+1]); else dfs(to,fa); } } int main() { n = read,m = read; init(); for(int i=1; i<=m; i++) { int u = read,v = read; add(u,v),add(v,u); deg[u] ++; deg[v] ++; } memset(vis,0,sizeof vis); if(n == m) Clear(in,1),topoSort(),flag = 1; memset(vis,0,sizeof vis); dfs(1,n+1); for(int i=1; i<=n; i++) { printf("%d%c",ans[i-1],i<n?' ':'\n'); } return 0; }
参考博客:https://blog.csdn.net/xyyxyyx/article/details/103010642