noip2018
小 Y 的旅行方案是这样的:
任意选定一个城市作为起点,然后从起点开始,
每次可以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。
当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。
需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。
为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 nn 的序列。
她希望这个序列的字典序 最小
对于 100% 的数据和所有样例, 1≤n≤5000
且 m = n − 1 或 m = n 。
题解:
因为只能回到上一个,所以当形状为树的时候,易知只能走dfs序
当形状为树上套一个环的时候,走法多样,无法简单便利,
所以我们考虑删除一条环上的边,用topo
很多预处理,很多细节,具体看代码
#include<cstdio> #include<cstdlib> #include<algorithm> #include<queue> using namespace std; int n,m; const int N=5003; int head[N],tot; struct node { int v,nx; }e[N<<1]; void add(int u,int v) { e[++tot].v =v,e[tot].nx =head[u],head[u]=tot; e[++tot].v =u,e[tot].nx =head[v],head[v]=tot; } int in[N]; int son[N<<1],cnt,st[N],ed[N]; void topo() { queue <int> q; for(int i=1;i<=n;i++) if(in[i]==1) q.push(i); while(!q.empty() ) { int u=q.front() ;q.pop() ; for(int i=st[u];i<=ed[u];i++) if(in[son[i]]>1) { in[son[i]]--; if(in[son[i]]==1) q.push(son[i]); } } } bool broken[N][N],change,fail; int ans[N],pos; void dfs(int rt,int f) { if(fail) return ; ++pos; if(change || !ans[pos] || ans[pos]>rt ) ans[pos]=rt,change=true; if( ans[pos]<rt ) { fail=true; return ; } for(int i=st[rt];i<=ed[rt];i++) if(son[i]!=f && !broken[rt][son[i]] ) dfs(son[i],rt); } void print() { for(int i=1;i<=n;i++) printf("%d ",ans[i]); printf(" "); } int main() { scanf("%d%d",&n,&m); int u,v; for(int i=1;i<=m;i++) scanf("%d%d",&u,&v), add(u,v); for(int i=1;i<=n;i++)//把son变成一段段排好序的序号,用于dfs { st[i]=cnt+1; for(int j=head[i];j;j=e[j].nx ) son[++cnt]=e[j].v ; ed[i]=cnt; sort(son+st[i],son+ed[i]+1); } if(m<n) pos=0,dfs(1,0),print(); else { for(int i=1;i<=n;i++) in[i]=ed[i]-st[i]+1; topo(); for(int i=1;i<tot;i+=2) { int u=e[i].v ,v=e[i+1].v ; if(in[u]==1 || in[v]==1) continue; broken[u][v]=broken[v][u]=true; fail=change=false; pos=0,dfs(1,0); broken[u][v]=broken[v][u]=false; } print(); } return 0; }
n^2的复杂度
https://www.cnblogs.com/mangoyang/p/9314823.html
https://www.luogu.org/problem/P5021
https://www.luogu.org/problem/P4654