Checkers HDU 3830 辗转相除+二分+LCA

Little X, Little Y and Little Z are playing checkers when Little Y is annoyed. So he wants to make the chessboard much bigger. Although Little Z insists the original version, Little X stands by Little Y. After they enlarge the chessboard, the chessboard turns to an infinite line. 
The chessboard is like the Number Axes now, with each integer point able to hold a checker. At initial status there are three checkers on three different integer points , and through the game there always are three checkers. Every time, they can choose a checker A to jump across a pivot checker B to a new position(but the distance between old A and B equals to new A and B, and there should be no other checkers except B in the range  oldA,newA oldA,newA). 
After playing for a while, they wonder whether an given status a,b,c can be transferred to x,y,z. obeying the rules. Since the checkers are considered the same, it is unnecessary for a must jump to x. 
Input
The first line is a,b,c. 
The second line is x,y,z. 
They are all integers in range (-10^9, 10^9) and the two status are valid. 
Output
The first line is YES or NO, showing whether the transfer can be achieved. 
If it is YES, then output the least steps in the second line. 
Sample Input
1 2 3
0 3 5
Sample Output
YES
2


        
  
Hint
The middle checker jumps to position 0, and the status is 0 1 3
Then , the middle one jumps to 5.
        
 

这道题目是一道非常好的思维题,把我震撼到了。

我们把每一个三元组状态(x,y,z)看成一个节点

从这个状态(通过中间棋子往两边跳)一定可以转移到另外两种状态(2x-y,x,z)和(x,z,2z-y)。这另外两种状态就是上一个状态的两个分支,上一个状态(x,y,z)就是这两个状态的父亲节点。

令d1 = y-x 

d2 = z - y 

其实还有一种转移的情况,那就是如果d1<d2的话,那么可以把x往中间跳,这样就相当于往该节点的父亲节点走。。。

通过上面的叙述,我们可以知道,所有状态组成了一个森林。我们要判断两个状态是否可以相互转换,只需要判断他们是否具有相同的根节点。

我们要求最少的步数,也就转化为了求他们的LCA。

这里求LCA的时候,我们考虑这样一种方法。

首先求出两个状态的深度,然后让一个比较深的节点先往上走|dep1 - dep2|的距离

然后二分深度p,其中check函数这样写,如果同时向上走p步以后,到达同一个节点,那么返回true,否则返回false。

这样答案就是ans = |dep1-dep2| + 2*p

为了不超时,我们在求深度以及往上走特定步数的时候,都要用辗转相除法。

#include <iostream>
#include <cstdio>
#include <algorithm> 
#include <map> 
using namespace std;
typedef long long LL;
LL X[3],Y[3];
int ans;
struct tpl{
	LL a,b,c;
	tpl(){
	}
	tpl(LL ta,LL tb,LL tc){
		a = ta,b = tb,c = tc;
	}
	friend bool operator <(const tpl t1,const tpl t2){
		if(t1.a == t2.a){
			if(t1.b == t2.b){
				return t1.c < t2.c;
			}else{
				return t1.b < t2.b;
			}
		}else{
			return t1.a < t2.a;
		}
	}
};
map<tpl,int> mp;
void find_root(LL array[]){
	sort(array,array+3);
	int cnt = 1;
	if(mp[tpl(array[0],array[1],array[2])]){
		ans = mp[tpl(array[0],array[1],array[2])] - 1;
		return ;
	}
	else{
		mp[tpl(array[0],array[1],array[2])] = cnt;
	}
	while(array[2] - array[1] != array[1] - array[0]){
		//tpl tp1 = tpl(array[0],array[1],array[2]);
		LL d1 = array[1] - array[0];
		LL d2 = array[2] - array[1];
		if(d1 < d2){
			array[0] = 2*array[1] - array[0];
		} 
		else{
			array[2] = 2*array[1] - array[2];
		}
		sort(array,array+3);
		tpl tp2 = tpl(array[0],array[1],array[2]);
		if(mp[tp2]){
			ans = mp[tp2] + ++cnt - 2;
			return ;
		}
		mp[tp2] = ++cnt;
	}
}
int main(){
	LL a,b,c;
	LL x,y,z;
	cin>>a>>b>>c>>x>>y>>z;
	X[0] = a,X[1] = b,X[2] = c;
	Y[0] = x,Y[1] = y,Y[2] = z;
	ans = -1;
	find_root(X);
	find_root(Y);
	//if(X[0] == Y[0] && X[1] == Y[1] && X[2] == Y[2]){
	if(ans != -1){
		puts("YES");
		cout<<ans<<endl;
	}
	else{
		puts("NO");
	}
	return 0;
} 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值