[国家集训队]跳跳棋

题目描述

跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。

我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)

跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。

写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。

输入输出格式

输入格式:

第一行包含三个整数,表示当前棋子的位置a b c。(互不相同)

第二行包含三个整数,表示目标位置x y z。(互不相同)

输出格式:

如果无解,输出一行NO。

如果可以到达,第一行输出YES,第二行输出最少步数。

输入输出样例

输入样例#1: 复制
1 2 3
0 3 5
输出样例#1: 复制
YES
2

说明

20% 输入整数的绝对值均不超过10

40% 输入整数的绝对值均不超过10000

100% 绝对值不超过10^9

【解题思路】

为了方便描述,我们把左边的棋子称为a,中间的棋子称为b,右边的为c。仔细观察跳棋规则,我们会发现当左右两跳棋到中间距离不等时有三种转移方式(因为不能跳过两个棋子)

  1. b往a方向跳
  2. b往c方向跳
  3. 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 }

 

posted @ 2019-07-22 23:14  GTR_PaulFrank  阅读(263)  评论(0编辑  收藏  举报