【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 }

 

转载于:https://www.cnblogs.com/HLAUV/p/9883140.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值