版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/88067071
给定一棵树(或者是基环树),求从一点出发遍历所有点(可以重复经过且无向)的字典序最小的序列是什么?
对于树的情况,显然贪心即可,时间复杂度: (排序的复杂度)
对于基环树的情况,暴力拆环找即可,时间复杂度
一些细节:
首先要对边排序呀,这样就可以比较方便的解决贪心的问题
然后找环用
接下来就暴力拆边跑 啦
#include<queue>
#include<cstdio>
#include<vector>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;int n,m,a[5001],len,t,ans[5001],x,y,rd[5001],root,fa;//a为当前遍历的集合,ans为最终答案,rd为入度,root为环上的任意一点
vector<int>g[5001];//邻接表数组,这里用vector是为了方便排序
inline long long read()
{
char c;int d=1;long long f=0;
while(c=getchar(),!isdigit(c))if(c==45)d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void dfs(int x,int fa,int nx,int ny)//当前遍历的点,上一次遍历的点,被删除的边连接的两个点
{
a[++len]=x;
for(register int i=0;i<g[x].size();i++)
{
int y=g[x][i];
if(y==fa||x==nx&&y==ny||x==ny&&y==nx) continue;
dfs(y,x,nx,ny);
}
}
inline bool comp()//比较当前情况是否最优
{
if(len<n) return false;//没有搜到
if(!ans[1]) return true;//第一次搜到
for(register int i=1;i<=n;i++) if(a[i]!=ans[i]) return a[i]<ans[i];
return false;
}
inline void topsort()//拓扑排序
{
queue<int>q;
int y,w;
for(register int i=1;i<=n;i++) if(!--rd[i]) q.push(i);
while(q.size())
{
int x=q.front();q.pop();
for(register int i=0;i<g[x].size();i++) if(!--rd[y=g[x][i]]) q.push(y);
}
return;
}
signed main()
{
n=read();m=read();
for(register int i=1;i<=m;i++) rd[x=read()]++,rd[y=read()]++,g[x].push_back(y),g[y].push_back(x);//输入+初始化
for(register int i=1;i<=n;i++) sort(g[i].begin(),g[i].end());//边排序
if(m<n)//是一棵树
{
dfs(1,0,0,0);
for(register int i=1;i<=n;i++) printf("%d ",a[i]);//暴力遍历然后输出
return 0;
}
topsort();//拓扑排序
for(register int i=1;i<=n;i++) if(rd[i]>0) {root=i;break;}//找到环上的点
x=root;fa=0;
do//遍历这个环
{
for(register int i=0;i<g[x].size();i++)
if(rd[y=g[x][i]]>0&&y!=fa)
{
len=0;
dfs(1,0,x,y);
if(comp()) memcpy(ans,a,sizeof(a));
fa=x;x=y;
break;
}
}while(x!=root);//直到遍历完这个环
for(register int i=1;i<=n;i++) printf("%d ",ans[i]);
}