题目大意: 有一颗无限的完全二叉树,现在其中有一个连通块有宝藏,你需要从根节点出发,用一个无限复读指令的机器人走完整个连通块,并且要求指令尽可能短。
题解
我们发现,每一次执行完指令后,机器人的位置与原位置会有一个固定的相对位移,我们不妨在连通块里面枚举一个终点,以根节点到终点这一段作为位移。显然,每次的终点都一定是当前根节点的子树里面的一个点。
对于一段位移,我们要怎么知道它实际走的路线呢?有这样一个性质:设根节点为 x x x,终点为 y y y,那么在 x x x 走到 y y y 的过程中,一定走完了 x x x 的子树中不属于 y y y 的子树的部分。证明很简单,因为如果不走完的话以后又不走回来,就达不成走完整个连通块的目标了。
那么我们将每一段位移分出来的一定要走完的部分合并起来(注释中称这个为并树),得到树的大小 s i z e size size,然后 2 × ( s i z e − 1 ) − x 2\times (size-1) -x 2×(size−1)−x( x x x 是指令长度) 就是当前位移的答案。因为这整棵树里面除去根节点不用走,根节点到终点这些点只用走一次,其他的点都要走两次(走下去在往上走回来)。
代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
string s;
struct node{
node *zuo,*you;//二叉树的左右儿子
node():zuo(NULL),you(NULL){}
int getsize()//求树的大小
{
int size=1;
if(zuo!=NULL)size+=zuo->getsize();
if(you!=NULL)size+=you->getsize();
return size;
}
};
node *root=new node(),*rt=NULL;
int t=0;
void build(node *x)//建树
{
switch(s[t])
{
case '0':
return; break;
case '1':
x->zuo=new node();
t++;build(x->zuo);
break;
case '2':
x->you=new node();
t++;build(x->you);
break;
case '3':
x->zuo=new node();
t++;build(x->zuo);
x->you=new node();
t++;build(x->you);
break;
}
}
void merge(node *a,node *b,node *limit)//将a,b两棵树合并,其中b这棵树只合并到limit位置为止
{
if(b==limit)return;
if(b->zuo!=NULL)
{
if(a->zuo==NULL)a->zuo=new node();
merge(a->zuo,b->zuo,limit);
}
if(b->you!=NULL)
{
if(a->you==NULL)a->you=new node();
merge(a->you,b->you,limit);
}
}
void dfs(node *now,string displacement)//从now位置不断向下位移
{
node *to=now;
for(int i=0;i<displacement.length();i++)
if(displacement[i]=='L')
{
if(to->zuo==NULL){to=NULL;break;}
to=to->zuo;
}
else
{
if(to->you==NULL){to=NULL;break;}
to=to->you;
}
merge(rt,now,to);//将其他部分合并
if(to!=NULL)dfs(to,displacement);
}
int ans=999999999;
void solve(string displacement)
{
rt=new node();//并树的初始化
dfs(root,displacement);
int size=rt->getsize();//求出大小
if(2*(size-1)-displacement.length()<ans)ans=2*(size-1)-displacement.length();
//更新答案
}
void go(node *now,string displacement)
{
if(now!=root)solve(displacement);//尝试以当前位移更新答案
if(now->zuo!=NULL)
go(now->zuo,displacement+"L");
if(now->you!=NULL)go(now->you,displacement+"R");
}
int main()
{
cin>>s;
build(root);
go(root,"");//枚举终点,即枚举位移
printf("%d",ans);
}