P5022 旅行 (NOIP2018)
先考虑是一颗树的情况
求最小的 dfs 序
显然按儿子编号从小到大dfs
如果有多一条边怎么办
显然会有一条边不用走
直接枚举删那条边然后每次都暴力 dfs
复杂度 $O(n^2)$
注意每个节点的儿子顺序先预处理好
不要每次都重新算
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<vector> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=10007; int fir[N],from[N<<1],to[N<<1],cntt;//存排序前的边 inline void add(int &a,int &b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; } int n,m,ans[N]; vector <int> v[N];//存排序后的边 int st[N],t,p1,p2; bool vis[N]; void dfs(int x)//暴力dfs求字典序 { st[++t]=x; vis[x]=1; int len=v[x].size(); for(int i=0;i<len;i++) { int &to=v[x][i]; if(vis[to]||(p1==x&&p2==to)||(p1==to&&p2==x)) continue; dfs(to); } } inline bool pd()//暴力比较字典序大小 { if(t<n) return 0; if(!ans[1]) return 1; for(int i=1;i<=n;i++) if(ans[i]!=st[i]) return ans[i]<st[i] ? 0 : 1; } int e[N][2],tmp[N],tot; int main() { freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); int a,b; n=read(); m=read(); for(int i=1;i<=m;i++) { a=read(); b=read(); add(a,b); add(b,a); e[i][0]=a; e[i][1]=b; } for(int i=1;i<=n;i++)//预处理儿子顺序 { tot=0; for(int j=fir[i];j;j=from[j]) tmp[++tot]=to[j]; sort(tmp+1,tmp+tot+1); for(int j=1;j<=tot;j++) v[i].push_back(tmp[j]); } if(m==n-1) { dfs(1); for(int i=1;i<=n;i++) printf("%d ",st[i]); return 0; } for(int i=1;i<=m;i++) { t=0; p1=e[i][0]; p2=e[i][1];//暴力删边 for(int i=1;i<=n;i++) vis[i]=0; dfs(1); if(pd()) for(int i=1;i<=n;i++) ans[i]=st[i]; } for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }