题目描述
跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。
我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)
跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。
写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。
输入输出格式
输入格式:
第一行包含三个整数,表示当前棋子的位置a b c。(互不相同)
第二行包含三个整数,表示目标位置x y z。(互不相同)
输出格式:
如果无解,输出一行NO。
如果可以到达,第一行输出YES,第二行输出最少步数。
输入输出样例
说明
20% 输入整数的绝对值均不超过10
40% 输入整数的绝对值均不超过10000
100% 绝对值不超过10^9
【解题思路】
为了方便描述,我们把左边的棋子称为a,中间的棋子称为b,右边的为c。仔细观察跳棋规则,我们会发现当左右两跳棋到中间距离不等时有三种转移方式(因为不能跳过两个棋子)
- b往a方向跳
- b往c方向跳
- a,c离b距离近的往里跳
a,c到b距离相等的时候只有1,2两种转移方式。
这TM不就是棵二叉树
往中间跳的是父亲,两旁的是儿子。
根就是没有父亲的节点(想一想,是什么)
现在就好做了,能不能到看根相不相同,移动次数就是他们到LCA的距离之和 ->LCA传送门
问题又来了,状态太多保存不下怎么办???
下面是重点!!!
首先要明白棋子是相同的,所以a,b,c保存的是相对位置,跳一次相当与把两个棋子平移dis,dis为它们之间的距离。我们设d1=b-a,d2=c-b。d1小于d2时我们移动a,然后会发现d1没变,d2减小了d1所以我们可以连续走d2/d1次,反之亦然,此时d2小于d1了换个方向走。注意:d2%d1等于0时走d2/d1-1步就到根了。
那么怎么计算路径呢
先把深度大的节点移到深度小的节点(深度在求根的时候可以顺便求出来)然后二分到LCA的距离,往上走n步和求根差不多这里就不废话了,上代码。
【code】
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define ll long long 8 #define inf 1000000000 9 using namespace std; 10 int read() { 11 int x=0,f=1;char ch=getchar(); 12 while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();} 13 while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();} 14 return x*f; 15 } 16 int t1,t2,tmp,ans; 17 int a[5],b[5]; 18 struct data {int a[5];}; 19 data cal(int *a,int k) { //得到a状态向上走k次的状态 20 data ans; 21 int t1=a[2]-a[1],t2=a[3]-a[2]; 22 for(int i=1; i<=3; i++)ans.a[i]=a[i]; 23 if(t1==t2)return ans; 24 if(t1<t2) { 25 int t=min(k,(t2-1)/t1); 26 k-=t; tmp+=t;//顺便记录深度 27 ans.a[2]+=t*t1; ans.a[1]+=t*t1; 28 } else { 29 int t=min(k,(t1-1)/t2); 30 k-=t; tmp+=t; 31 ans.a[2]-=t*t2; ans.a[3]-=t*t2; 32 } 33 if(k)return cal(ans.a,k);//辗转相除 34 else return ans; 35 } 36 bool operator!=(data a,data b) { 37 for(int i=1; i<=3; i++)if(a.a[i]!=b.a[i])return 1; 38 return 0; 39 } 40 int main() { 41 for(int i=1; i<=3; i++)a[i]=read(); 42 for(int i=1; i<=3; i++)b[i]=read(); 43 sort(a+1,a+4); sort(b+1,b+4); 44 data t1=cal(a,inf); int d1=tmp; tmp=0; 45 data t2=cal(b,inf); int d2=tmp; tmp=0; 46 //t1,t2分别为a,b的根,d1,d2为深度 47 if(t1!=t2) { puts("NO"); return 0; } 48 if(d1>d2) { 49 swap(d1,d2); 50 for(int i=1; i<=3; i++)swap(a[i],b[i]); 51 } 52 ans=d2-d1; 53 t1=cal(b,ans); 54 for(int i=1; i<=3; i++)b[i]=t1.a[i]; //较深的向上调整 55 int l=0,r=d1; 56 while(l<=r) { //二分 57 int mid=(l+r)>>1; 58 if(cal(a,mid)!=cal(b,mid))l=mid+1; 59 else r=mid-1; 60 } 61 puts("YES"); 62 printf("%d",ans+2*l); 63 return 0; 64 }