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
HINT
【范围】
100% 绝对值不超过10^9
题解
神题..
很容易发现一个三元组只有三种跳法
b往左边/右边跳 或者距离b较近的那个往里跳
如果a,c距离b的距离一样,那么只有两种跳法
下来就很神奇了!
我们把距离b较近那个往里跳转移到的状态设为这个状态的父亲节点
另外两个转移设为孩子节点
a,c距离b的距离一样的作为根
发现这可以构成一棵树!!!
那不就是求树上两点之间路径和嘛..
首先判一下两个状态是否在同一个根下,不是输出NO
考虑模拟一个倍增的过程
设b距离a为n1 c距离b为n2 不妨仅讨论n1 < n2的情况
此时可以向右跳动(n2-1)/n1次
先将两个点移到相同深度,然后二分答案判断即可..
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
int a,b,c;
node(){}
node(int _a,int _b,int _c){a=_a;b=_b;c=_c;}
}g1,g2;
int sum;
void jump(node &g,int k)
{
int n1=g.b-g.a,n2=g.c-g.b;
if(!k)return ;
if(n1==n2)return ;
if(n1<n2)
{
int p=min(k,(n2-1)/n1);k-=p;sum+=p;
g.b=g.b+n1*p;g.a=g.b-n1;
}
else
{
int p=min(k,(n1-1)/n2);k-=p;sum+=p;
g.b=g.b-n2*p;g.c=g.b+n2;
}
if(!k)return ;
jump(g,k);
}
bool chk(node g1,node g2)
{
if(g1.a!=g2.a||g1.b!=g2.b||g1.c!=g2.c)return false;
return true;
}
bool check(int p)
{
node p1=g1,p2=g2;
jump(p1,p);jump(p2,p);
return chk(p1,p2);
}
int u[4];
int main()
{
node u1,u2;
scanf("%d%d%d",&u[1],&u[2],&u[3]);sort(u+1,u+1+3);
u1=g1=node(u[1],u[2],u[3]);
scanf("%d%d%d",&u[1],&u[2],&u[3]);sort(u+1,u+1+3);
u2=g2=node(u[1],u[2],u[3]);
sum=0;jump(u1,(1<<31-1));int s1=sum;
sum=0;jump(u2,(1<<31-1));int s2=sum;
if(!chk(u1,u2)){printf("NO\n");return 0;}
if(s1>s2)swap(g1,g2),swap(s1,s2);
jump(g2,s2-s1);
int l=0,r=s1,ans;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
puts("YES");
printf("%d\n",s2-s1+2*ans);
return 0;
}