题解 P1852 【跳跳棋】
不得不说,这题建模极为抽象了,出题人太神了。看了好多篇题解才懂,自己也写一篇比较详细的吧
相信大家都玩过跳棋,有一个规则想必大家都知道,就是一次跳跃不能跳过两颗棋子
这个限制是解决这题的关键,有了这个限制,可以看出,对于两边的棋子,只能跳到其余两颗的中间,同样的中间的棋子也只能跳到两边
为了方便,我们将三个棋子标号,分别为
移动棋子无非就四种讨论
-
\large(x,y,z)\implies(2x-y,x,z) -
\large(x,y,z)\implies(x,z,2z-y) -
\large(x,y,z)\implies(y,2y-x,z) -
\large(x,y,z)\implies(x,2y-z,y)
因为有了不能一次跳过两颗棋的限制,所以第三种情况和第四种情况必然起了冲突
不妨设
-
d1<d2:$则 $(x,y,z)\implies(y,2y-x,z) -
d1>d2:$则 $(x,y,z)\implies(x,2y-z,y)
也就是说我们对于每种状态只有三种转移方式
我们再看看这三个式子,可以发现,对于第一种和第二种情况,我们将中间的点向两边跳,相当于扩大了边界。而第三种两边向里面跳则恰恰相反,是将边界缩小了
是不是很像一棵二叉树?
现在可能还不够明显,我们再接下去推
有一个很显然的性质,边界不可能无限变小。也就是说缩着缩着会遇到一种情况不能再缩了,我们来思考一下什么情况不能再缩了
观察式子:
-
d1<d2:$则 $(x,y,z)\implies(y,2y-x,z) -
d1>d2:$则 $(x,y,z)\implies(x,2y-z,y)
惊奇的发现,我们这样讨论漏了一种情况,
这个非常好理解,如果
可以发现,对于每一组
所以如果初始
所以对于初始
不过因为范围太大,树无法存下,普通的
我们先不急着求
初始状态:
目标状态:
人脑模拟一下程序,
如果改成
显然要
不过我们发现,一直在重复一个操作,所以可以将这些操作压缩
我们只讨论
易发现,要连续跳
设:
所以最后是
这样我们就大大的压缩了路径,但这样又有什么用呢?我们回到树上来
因为路径被压缩了,所以我们可以换种方法求
方便起见,我们先将初始状态跳到和目标状态在树中的高度一致(如果目标状态高度更高我们就换一下,在树中的距离是没有方向的)
也是初始状态要向上跳
好了,现在两个状态高度相同了
有个显然的性质,如果两个状态同时向上跳
然后就愉快的
码量小但细节是真的多,思维难度也很好,总而言之就是一道质量非常高的建模
如果有哪步我打错了,或者有一些没有理解的地方,欢迎私信或留言!
参考代码:
#include<bits/stdc++.h>
using namespace std;
int Cnt,Ans,d1,d2,d3,x,y,z,L,R,X,Y,Z,tot,a[5];
struct lc{
int x,y,z,tot;
}A,B;
inline int read(){
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-f;ch=getchar();}
while (ch<='9'&&ch>='0') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
inline void Dfs(int x,int y,int z){
tot=0;
while (1){
d1=y-x,d2=z-y;
if (d1==d2) break;
if (d1>d2) d3=(d1-1)/d2,tot+=d3,y-=d3*d2,z-=d3*d2;
else d3=(d2-1)/d1,tot+=d3,x+=d3*d1,y+=d3*d1;
}
if (A.x||A.y) B=(lc){x,y,z,tot};
else A=(lc){x,y,z,tot};
}
inline void Swap(){
swap(x,X),swap(y,Y),swap(z,Z);
swap(A.x,B.x),swap(A.y,B.y),swap(A.z,B.z),swap(A.tot,B.tot);
}
inline bool check(int T,int x,int y,int z,int X,int Y,int Z){
tot=T;
while (tot){
d1=y-x,d2=z-y;
if (d1==d2) break;
if (d1>d2) d3=min((d1-1)/d2,tot),tot-=d3,y-=d3*d2,z-=d3*d2;
else d3=min((d2-1)/d1,tot),tot-=d3,x+=d3*d1,y+=d3*d1;
}
tot=T;
while (tot){
d1=Y-X,d2=Z-Y;
if (d1==d2) break;
if (d1>d2) d3=min((d1-1)/d2,tot),tot-=d3,Y-=d3*d2,Z-=d3*d2;
else d3=min((d2-1)/d1,tot),tot-=d3,X+=d3*d1,Y+=d3*d1;
}
return X==x&&Y==y&&Z==z;
}
int main(){
for (int i=1;i<=3;i++) a[i]=read();
sort(a+1,a+4);x=a[1],y=a[2],z=a[3];
for (int i=1;i<=3;i++) a[i]=read();
sort(a+1,a+4);X=a[1],Y=a[2],Z=a[3];
Dfs(x,y,z);Dfs(X,Y,Z);
if (A.x!=B.x||A.y!=B.y||A.z!=B.z){printf("NO");return 0;}
if (A.tot<B.tot) Swap();
Ans=tot=A.tot-B.tot;
while (tot){
d1=y-x,d2=z-y;
if (d1==d2) break;
if (d1>d2) d3=min((d1-1)/d2,tot),tot-=d3,y-=d3*d2,z-=d3*d2;
else d3=min((d2-1)/d1,tot),tot-=d3,x+=d3*d1,y+=d3*d1;
}
L=0,R=B.tot;
while (L<=R){
int mid=L+R>>1;
if (check(mid,x,y,z,X,Y,Z)) Cnt=mid,R=mid-1;
else L=mid+1;
}
printf("YES\n%d",Ans+Cnt*2);
return 0;
}