题意
有一棵二叉树,现在可以选定任意一个点为根,要求使得这棵二叉树的中序遍历字典序最小。
分析
不难发现若一个点度数小于3则一定可以成为第一个点。
那么我们可以先确定第一个点然后树形dp出以每个点为根编号最小且度数小于3的点。
然后从第一个点开始贪心,若走到某一棵子树内会使得答案更优,则走过去,不然就不走。
确定根后直接输出答案即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=1000005;
int n,cnt,last[N],deg[N],rt,mn[N];
struct edge{int to,next;}e[N*2];
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*10+ch-'0';ch=getchar();}
return x*f;
}
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
}
void pre(int x,int fa)
{
if (deg[x]<3&&x!=rt) mn[x]=x;
else mn[x]=n;
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa) pre(e[i].to,x),mn[x]=std::min(mn[x],mn[e[i].to]);
}
void dfs(int x,int fa)
{
if (x!=rt&°[x]==1) {rt=x;return;}
if (deg[x]<3) rt=x;
int son1=0,son2=0;
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa) son2=son1,son1=e[i].to;
if (son2&&mn[son2]>mn[son1]) std::swap(son1,son2);
if (deg[x]==3) dfs(son1,x);
else if (deg[son1]==2&&son1<mn[son1]) dfs(son1,x);
else if (deg[son1]==3)
{
int w=n;
for (int i=last[son1];i;i=e[i].next)
if (e[i].to!=x) w=std::min(w,mn[e[i].to]);
if (son1<w) dfs(son1,x);
}
}
void pri(int x,int fa)
{
int son1=0,son2=0;
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa) son2=son1,son1=e[i].to;
if (son2&&mn[son2]<mn[son1]) std::swap(son1,son2);
if (!son2&&mn[son1]>x) std::swap(son1,son2);
if (son1) pri(son1,x);
printf("%d ",x);
if (son2) pri(son2,x);
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
deg[i]=read();
for (int j=1;j<=deg[i];j++)
{
int x=read();
addedge(i,x);
}
}
rt=n;
for (int i=1;i<=n;i++) if (deg[i]<3) rt=std::min(rt,i);
pre(rt,0);
if (deg[rt]==1) dfs(rt,0);
else
{
int son1=0,son2=0;
for (int i=last[rt];i;i=e[i].next) son2=son1,son1=e[i].to;
if (mn[son1]<mn[son2]) std::swap(son1,son2);
dfs(son1,rt);
}
pre(rt,0);
pri(rt,0);
return 0;
}