XR-4 复读 题解

题目传送门

题目大意: 有一颗无限的完全二叉树,现在其中有一个连通块有宝藏,你需要从根节点出发,用一个无限复读指令的机器人走完整个连通块,并且要求指令尽可能短。

题解

我们发现,每一次执行完指令后,机器人的位置与原位置会有一个固定的相对位移,我们不妨在连通块里面枚举一个终点,以根节点到终点这一段作为位移。显然,每次的终点都一定是当前根节点的子树里面的一个点。

对于一段位移,我们要怎么知道它实际走的路线呢?有这样一个性质:设根节点为 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×(size1)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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值