[bzoj2144]: 跳跳棋

2144: 跳跳棋

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 689   Solved: 326
[ Submit][ Status][ Discuss]

Description

跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。  写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。

Input

第一行包含三个整数,表示当前棋子的位置a b c。(互不相同)第二行包含三个整数,表示目标位置x y z。(互不相同)

Output

如果无解,输出一行NO。如果可以到达,第一行输出YES,第二行输出最少步数。

Sample Input

1 2 3
0 3 5

Sample Output

YES
2


【范围】
100% 绝对值不超过10^9

思路
bzoj 2144 跳跳棋 2017.6.2 by xlj 
难题一道 很不好想 
从题目条件中看似6种状态 
但因为一个棋子不能跳过两个棋子
如果 1 4 10 此时右边的就不能跳 因为越过两个棋子了 
所以只有三种(中间的可以跳两边&离中间棋子距离近的可以跳) 
可以把这三种状态看成一棵树
此时可以设棋子为x,y,z; 
设y-x=t1 z-y=t2
当t1==t2的时候就没有办法跳了 这个状态就是树根了
如果t1t1 同理 
然后类似gcd的方法求树根 (t1,t2)->(t1-t2,t2) 
从给出的a1,b1,c1往上推出数根
a2,b2,c2也是一样
如果树根不同 代表不能完成 输出NO
如果相同 先把两个状态深度调为一样 再往上找(LCA) 

这里用的是二分法找 

#include
#include
#include
#include
#include
#include
#define inf 1000000000
using namespace std;
int a[5],b[5];
struct node{
	int a[5];
};
int tmp,ans;
node cal(int *a,int k)//在树上往上跳的 函数 k是跳的步数 跳到根就结束 
{
	node ans;
	int t1=a[2]-a[1];int t2=a[3]-a[2];
	for(int i=1;i<=3;i++) ans.a[i]=a[i];
	if(t1==t2) return ans;
	if(t1d2)
    {
		swap(d1,d2);
		for(int i=1;i<=3;i++)swap(a[i],b[i]);
    }
    ans=d2-d1;//深度大的就往上调 
    t1=cal(b,ans);
    for(int i=1;i<=3;i++) b[i]=t1.a[i];
    int l=0,r=d1;
    while(l<=r)//二分法找LCA 
    {
		int mid=(l+r)>>1;
		if(cal(a,mid)!=cal(b,mid))l=mid+1;//不等就网上找 
		else r=mid-1;//如果相等了 还可能在答案那个节点的下面 
    }
    puts("YES");
    printf("%d",ans+2*l);
    return 0;
}



你可能感兴趣的:(LCA,树,数据结构,算法,bzoj)