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
不得不说这题真是tm坑爹想法题,好写不好想考场上很难A掉
如果单纯排列搞一搞貌似有6种走法,但是因为棋子跳的时候只能越过一个棋子,所以对于任意一个状态,它最多只有3种走法。
假设三个点已经从小到大排好,显然中间的棋子无论往左跳还是往右跳都只要越过一个棋子。
考虑最左边的棋子,它不可能直接越过中间和右边的棋子。右边同理。
现在问题来了。两边的棋子越过中间的点是否会直接越过两个点呢?
以2、3、7为例:
2跳过3到达4,是不会超过7的。
但是7越过3到达-1,显然超过了2。所以它跳过了两个棋子,这是不合法的。
这样就得到了结论:只有离中间更近的棋子才可以跳进去。那么确实是最多只有3种情况。
为什么是最多呢?因为还有两边与中间的距离一样近的情况,这样左右都跳不了了,只有中间的可以跳出去。比如3、5、7。
很容易发现,从只有两种跳法的所有状态出发,就可以到达任意一种状态。
什么意思呢?这是一棵二叉树。从2、4、6这样的状态出发,只能4往左右走。把拓展的状态作为左右儿子。左边0、2、6,右边2、6、8。在这两个状态中,如果我们从两边往中间走,显然就还原为父亲节点的状态了。这样是没有意义的。于是只好再从中间完两边走,然后又变成二叉树……于是所有的状态就表示成了以形如等差数列三项的状态为根构成的森林。
这样一来,只要知道两个状态所在的树根是不是一样就可以判断是否有解。具体解的大小可以LCA搞定,或者直接二分答案
假设三个数a、b、c,b-a=s1,c-b=s2。不妨设s1<s2。我们的目标就是把a、c往中间移,最终使得s1=s2。
那么显然a往中间跳的次数就是(s1-1)/s2
而且a、b、c是不计顺序的,所以直接a+=s1*(s1-1)/s2 b+=s1*(s1-1)/2 就好了
时间复杂度大概和gcd是同级的,每次logn
(写了这么长啊……为什么换了个博客废话多起来)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<deque> 9 #include<set> 10 #include<map> 11 #include<ctime> 12 #define LL long long 13 #define inf 0x7ffffff 14 #define pa pair<int,int> 15 #define pi 3.1415926535897932384626433832795028841971 16 using namespace std; 17 struct zt{ 18 int a,b,c,dep; 19 }st,nd,rotst,rotnd; 20 inline void setmax(int &a,int &b,int &c) 21 { 22 int mx=a;if (mx<b)mx=b;if (mx<c)mx=c; 23 int mn=a;if (mn>b)mn=b;if (mn>c)mn=c; 24 b=a+b+c-mx-mn; 25 a=mn; 26 c=mx; 27 } 28 bool operator !=(const zt &a,const zt &b) 29 {return a.a!=b.a||a.b!=b.b||a.c!=b.c;} 30 bool operator ==(const zt &a,const zt &b) 31 {return a.a==b.a&&a.b==b.b&&a.c==b.c;} 32 int l,r,ans; 33 inline LL read() 34 { 35 LL x=0,f=1;char ch=getchar(); 36 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} 37 while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} 38 return x*f; 39 } 40 inline void inpu(zt &a) 41 { 42 a.a=read();a.b=read();a.c=read(); 43 setmax(a.a,a.b,a.c); 44 } 45 inline void outpu(zt a) 46 {printf("%d %d %d %d\n",a.a,a.b,a.c,a.dep);} 47 inline void cal(zt &a) 48 { 49 int dea=a.b-a.a; 50 int deb=a.c-a.b; 51 if (dea==deb)return; 52 if (dea<deb) 53 { 54 int des=(deb-1)/dea; 55 a.dep+=des; 56 a.a+=des*dea; 57 a.b+=des*dea; 58 cal(a); 59 }else 60 if (dea>deb) 61 { 62 int des=(dea-1)/deb; 63 a.dep+=des; 64 a.c-=des*deb; 65 a.b-=des*deb; 66 cal(a); 67 } 68 } 69 inline zt gok(zt a,int k) 70 { 71 int dea=a.b-a.a; 72 int deb=a.c-a.b; 73 if (dea==deb||!k)return a; 74 if (dea<deb) 75 { 76 int des=min((deb-1)/dea,k); 77 k-=des; 78 a.dep+=des; 79 a.a+=des*dea; 80 a.b+=des*dea; 81 if (k)return gok(a,k); 82 else return a; 83 }else 84 if (dea>deb) 85 { 86 int des=min((dea-1)/deb,k); 87 k-=des; 88 a.dep+=des; 89 a.c-=des*deb; 90 a.b-=des*deb; 91 if (k)return gok(a,k); 92 else return a; 93 } 94 } 95 inline bool jud(int k) 96 { 97 return gok(st,k)==gok(nd,k); 98 } 99 int main() 100 { 101 inpu(st);setmax(st.a,st.b,st.c);rotst=st; 102 inpu(nd);setmax(nd.a,nd.b,nd.c);rotnd=nd; 103 cal(rotst); 104 cal(rotnd); 105 if (rotst!=rotnd) 106 { 107 printf("NO\n"); 108 return 0; 109 } 110 if (rotst.dep<rotnd.dep)swap(st,nd); 111 int dess=abs(rotst.dep-rotnd.dep); 112 st=gok(st,dess); 113 l=0;r=min(rotst.dep,rotnd.dep); 114 while (l<=r) 115 { 116 int mid=(l+r)>>1; 117 if (jud(mid)){ans=mid;r=mid-1;} 118 else l=mid+1; 119 } 120 printf("YES\n%d\n",dess+2*ans); 121 }
bzoj2144