【BZOJ2144】跳跳棋
【题目 描述】
跳跳棋是在一条数轴上进行的。 棋子只能摆在整点上。 每个点不能摆超过一
个棋子。 我们用跳跳棋来做一个简单的游戏: 棋盘上有 3 颗棋子, 分别在 a, b,
c 这三个位置。 我们要通过最少的跳动把他们的位置移动成 x, y, z。 (棋子是
没有区别的) 跳动的规则很简单, 任意选一颗棋子, 对一颗中轴棋子跳动。 跳动
后两颗棋子距离不变。 一次只允许跳过 1 颗棋子。
写一个程序, 首先判断是否可以完成任务。 如果可以, 输出最少需要的跳动次数。
【输入格式】
第一行包含三个整数, 表示当前棋子的位置 a b c。 (互不相同)
第二行包含三个整数, 表示目 标位置 x y z。 (互不相同)
【输出格式】
如果无解, 输出一行 NO。 如果可以到达, 第一行输出 YES, 第二行输出最少步数。
【样例输入】
1 2 3
0 3 5
【样例输出】
YES
2提示
100% 绝对值不超过 10^9
刚开始看到这道题真的没什么想法,真是道神秘的好题啊(~)
后来在草稿上自己画了几种状态,发现(!)每一种状态,只能发展出三种状态, 1.中间向左跳 2.中间向右跳 3.两边的某一个向中间跳(当然根节点状态不行)
如果把一种状态疯狂地向中间折叠,会跳到一个平衡的状态,两边再也无法向中间跳了,这个状态其实就是根,而从这个状态向外发展出去,每次都只会发展出两种,所以发现这些状态构成了一棵二叉树,只要任意两种状态都能变成相同的根节点状态,那么它们一定可以相互转化(!)
所以问题就转化成求树上两个不同节点的距离了。
我先让两个节点跳到一个相同的深度,再二分距离,让这两个节点同时向上跳,最后得出答案。
哦对了,向上跳(向内折叠)时发现可以用除法来加速,一下子可以折好大一段,没有必要每一次都是加法模拟(那样会超时的)
(不知为什么我的代码很长)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 8 #define For(i,a,b) for(register int i=a;i<=b;++i) 9 #define Re register 10 using namespace std; 11 struct Bal{ 12 int x,y,z; 13 }b1,b2,rb1,rb2,bx1,bx2; 14 int tp1,tp2; 15 inline void read(int &v){ 16 v=0; bool fg=0; 17 char c=getchar(); if(c=='-')fg=1; 18 while(c<'0'||c>'9'){c=getchar(); if(c=='-')fg=1;} 19 while(c>='0'&&c<='9'){v=v*10+c-'0',c=getchar(); if(c=='-')fg=1;} 20 if(fg)v=-v; 21 } 22 23 void getNT(Bal &b){ //使x,y,z有序 24 if(b.x>b.y)swap(b.x,b.y); 25 if(b.x>b.z)swap(b.x,b.z); 26 if(b.y>b.z)swap(b.y,b.z); 27 } 28 29 int ComeBack(Bal &b){ //跳到根节点 30 int stp=0; 31 getNT(b); 32 while(b.x+b.z!=b.y*2){ 33 int d1=b.y-b.x; 34 int d2=b.z-b.y; 35 if(d1<d2){ 36 int tp=d2/d1; 37 if(d2%d1==0)tp--; 38 b.x+=tp*d1; 39 b.y+=tp*d1; 40 if(b.x>b.y)swap(b.x,b.y); 41 stp+=tp; 42 }else{ 43 int tp=d1/d2; 44 if(d1%d2==0)tp--; 45 b.y-=tp*d2; 46 b.z-=tp*d2; 47 if(b.z<b.y)swap(b.z,b.y); 48 stp+=tp; 49 } 50 } 51 return stp; 52 } 53 54 bool QL(Bal a,Bal b){ 55 if(a.x==b.x&&a.y==b.y&&a.z==b.z)return 1; 56 return 0; 57 } 58 59 Bal CheckandGo(Bal bl,int Lim){ //二分出可以向上跳的距离然后向上跳 60 Bal b=bl; 61 int Lm=Lim; 62 getNT(b); 63 while(Lm){ 64 int d1=b.y-b.x; 65 int d2=b.z-b.y; 66 if(d1<d2){ 67 int tp=d2/d1; 68 if(d2%d1==0)tp--; 69 if(tp>Lm)tp=Lm; 70 b.x+=tp*d1; 71 b.y+=tp*d1; 72 if(b.x>b.y)swap(b.x,b.y); 73 Lm-=tp; 74 }else{ 75 int tp=d1/d2; 76 if(d1%d2==0)tp--; 77 if(tp>Lm)tp=Lm; 78 b.y-=tp*d2; 79 b.z-=tp*d2; 80 if(b.z<b.y)swap(b.z,b.y); 81 Lm-=tp; 82 } 83 if(Lm==0)break; 84 } 85 return b; 86 } 87 88 int main(){ 89 // freopen("hop.in","r",stdin); 90 // freopen("hop.out","w",stdout); 91 read(b1.x); read(b1.y); read(b1.z); 92 read(b2.x); read(b2.y); read(b2.z); 93 94 getNT(b1); getNT(b2); 95 bx1=b1; bx2=b2; 96 tp1=ComeBack(bx1); tp2=ComeBack(bx2); 97 98 99 if(!QL(bx1,bx2)){ 100 printf("NO"); 101 }else{ 102 int bs=abs(tp1-tp2); 103 int l=0,r=min(tp1,tp2),ff; 104 105 if(tp1<tp2){//跳到同一深度 106 b2=CheckandGo(b2,bs); 107 }else{ 108 b1=CheckandGo(b1,bs); 109 } 110 111 while(l<=r){ //二分 112 int m=(l+r)>>1; 113 bx1=CheckandGo(b1,m); 114 bx2=CheckandGo(b2,m); 115 if(QL(bx1,bx2))ff=m,r=m-1; 116 else l=m+1; 117 } 118 cout<<"YES"<<endl; 119 cout<<bs+ff*2<<endl; 120 } 121 return 0; 122 }