D30 基环树 遍历最小字典序 P5022 [NOIP2018 提高组] 旅行
视频链接:D30 基环树 P5022 [NOIP2018 提高组] 旅行_哔哩哔哩_bilibili
基环树的遍历最小字典序问题:出边排序。暴力断边。剪枝优化。
// 基环树 遍历最小字典序 暴力 O(n^2) #include<bits/stdc++.h> using namespace std; const int N=5010; int n,m,a,b; vector<int> e[N]; pair<int,int> edge[N]; int du,dv,vis[N],cnt,better; vector<int> path(N,N); void dfs1(int x){ //树搜索 vis[x]=1; path[cnt++]=x; for(int y:e[x])if(!vis[y]) dfs1(y); } bool dfs2(int x){ if(!better){ //剪枝:若序号变大则回退,变小则走完 if(x>path[cnt]) return 1; if(x<path[cnt]) better=1; } vis[x]=1; path[cnt++]=x; for(int y:e[x]){ if(vis[y])continue; if(y==du&&x==dv)continue; if(y==dv&&x==du)continue; if(dfs2(y)) return 1; } return 0; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); e[a].push_back(b); e[b].push_back(a); edge[i]={a,b}; } for(int i=1;i<=n;i++)sort(e[i].begin(),e[i].end()); //出边排序 if(m==n-1) dfs1(1); else{ for(int i=1;i<=m;i++){ //暴力断边 du=edge[i].first; dv=edge[i].second; memset(vis,0,sizeof vis); cnt=better=0; dfs2(1); } } for(int i=0;i<n;i++)printf("%d ",path[i]); }
P5049 [NOIP 2018 提高组] 旅行 加强版 - 洛谷
// 基环树 遍历最小字典序 O(nlogn) #include<bits/stdc++.h> using namespace std; const int N=500010; int n,m,vis[N],path[N],fa[N],inc[N],yzd,cnt,tmp,yhs; vector<int> e[N]; void dfs1(int x){ //树搜索 vis[x]=1; path[++cnt]=x; for(int y:e[x])if(!vis[y]) dfs1(y); } void find(int x,int f){ //找环 if(yzd) return; if(fa[x]){ while(f!=x){ inc[f]=1; f=fa[f]; } inc[x]=1; //在环上 yzd=1; //已找到 return; } fa[x]=f; for(int y:e[x]) if(y!=f) find(y,x); } void dfs2(int x){ //环树搜索 vis[x]=1; path[++cnt]=x; if(inc[x]){ //x在环上 int flag=0; for(int i=0;i<e[x].size();i++){ //枚举x的出边点 if(yhs) break; int y=e[x][i]; if(vis[y]) continue; if(inc[y]){ //y在环上 i++; if(vis[e[x][i]]) i++; //跳过环的入口点 if(i<e[x].size()) tmp=e[x][i]; //环点x的未访问的第一个出边点 else if(y>tmp){ //环点y大于前面环点x的出边点,该回去访问了 flag=1; //找到回溯点 yhs=1; //已回溯,环上仅回溯这一次 } break; } } for(int y:e[x]){ if(vis[y]) continue; if(inc[y]&&flag) continue; //不访问环点y,从此处回溯 dfs2(y); } } else{ //x在环外 for(int y:e[x])if(!vis[y]) dfs2(y); } } int main(){ scanf("%d%d",&n,&m); for(int i=1,u,v; i<=m; i++){ scanf("%d%d",&u,&v); e[u].emplace_back(v); e[v].emplace_back(u); } for(int i=1;i<=n;i++) sort(e[i].begin(),e[i].end()); //出边排序 if(m==n-1) dfs1(1); else{ find(1,1); tmp=0x3f3f3f3f; dfs2(1); } for(int i=1; i<=n; i++) printf("%d ",path[i]); }
浙公网安备 33010602011771号