正题
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P5022
题目大意
一棵树(可能是基环树),从1出发,每到达一个新的点就记录下编号。求一种走法使得记录下来的编号字典序最小。
解题思路
我们先不考虑基环树。我们可以发现每次走字典序小的点就好了。我们可以排序一下就可以做到这点。
之后我们考虑基环树,我们暴力删边将基环树变为一棵普通的树。然后计算答案。
时间复杂度:O(n2)O(n^2)O(n2)
code
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define N 5010
using namespace std;
struct node{int to,next;
}a[2*N];
struct line{int x,y;
}l[2*N];
int tot,n,m,t,ls[N],in[N],state[N],w[N],ans[N],x,y,q[N];
bool k[N][N],v[N];
void addl(int x,int y)//加边
{a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;in[y]++;
}
bool topsort(){int l=0,r=0;for (int i=1;i<=n;i++) if(in[i]==1) q[++r]=i;while(l<r) {int now=q[++l];for (int i=ls[now];i;i=a[i].next){int y=a[i].to;if(in[y]>1){in[y]--;if(in[y]==1) q[++r]=y;}}}if(r==n) return true;return false;
}//拓扑求环
bool cmp(line x,line y)
{return x.y>y.y;}
void dfs(int x)//走一遍
{state[++t]=x;v[x]=true;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(k[x][y]||v[y]) continue;dfs(y);}
}
void check()//判断是否为更小字典序
{int i;bool flag=false;for(i=1;i<=n;i++)if(state[i]<ans[i]){flag=true;break;}else if(state[i]>ans[i]) return;if(!flag) return;for(;i<=n;i++)ans[i]=state[i];
}
void get_ans(int xs)//暴力删边
{int x=xs,b=0,i,last=0;do{w[++b]=x;in[x]=1;for(i=ls[x];i;i=a[i].next){int y=a[i].to;if(in[y]>1){x=y;break;}}}while(i);//记录环的每个点w[++b]=xs;for(int i=1;i<b;i++)//枚举删除的边{k[w[i]][w[i+1]]=k[w[i+1]][w[i]]=true;memset(v,0,sizeof(v));t=0;dfs(1);check();k[w[i]][w[i+1]]=k[w[i+1]][w[i]]=false;}
}
int main()
{memset(ans,127/3,sizeof(ans));scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d",&x,&y);l[i]=(line){x,y};l[i+m]=(line){y,x};}sort(l+1,l+1+2*m,cmp);//排序tot=1;for(int i=1;i<=2*m;i++)//加边{addl(l[i].x,l[i].y);}if(m==n-1)//普通的树{dfs(1);for(int i=1;i<=n;i++)printf("%d ",state[i]);return 0;}topsort();for(int i=1;i<=n;i++)if(in[i]>1){get_ans(i);break;}for(int i=1;i<=n;i++)printf("%d ",ans[i]);
}