跳跳棋

跳跳棋 洛谷

题目描述

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

我们用跳跳棋来做一个简单的游戏:棋盘上有 \(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\)

设三个点的位置为 \(x\)\(y\)\(z\),且 \(x<y<z\)

这时的移动方式理论上有六种,但是因为只能跨越一个点,所以 \(x->z\)\(z->x\) 是不行的。

不过也不是四种,如果 \(y-x<z-y\),那么 \(x->y\) 就会跨越两个点,所以不行。

综上,可以分为三种情况:

  1. \(y-x<z-y\),要么中间往两边跳,要么左边往右跳
  2. \(y-x>z-y\),要么中间往两边跳,要么右边往左跳
  3. \(y-x=z-y\),只能中间往两边跳

这时我们注意到,如果是两边往中间跳,它会不断跳下去,直到 \(y-x=z-y\),就必须往左右跳。

那么 YESNO 我们就可以判断了,让起点和终点不断往里面跳(不断缩小),直到不能往里跳为止。

如果此时两个状态相等,就说明至少可以通过这个最小的状态来转换(因为行走方式是可逆的,我可以从终点走到这里,就说明可以从这里走到终点),否则是 NO

但是题目还要求我们输出最短路径,而最小状态不一定是最短路径,因为有可能在到达最小状态时,就已经有两状态相等的情况了。

很明显,这是一颗二叉树,根节点为最小状态,它的左儿子为中间向左跳,右儿子为中间向右跳。

那么两个状态就可以在树中找到自己的位置(前提是已经判断好了最小状态相等),然后我们发现找最小状态就是判断根节点是否相等。

那么最短路径当然就是求 \(LCA\),求就行了。

但是,由于树实在太大了,普通的倍增,树剖求 \(LCA\) 肯定不行,如果暴力跳,那么时间复杂度会爆炸。

不过我们发现,如果往上面找父亲时一直往一个方向走,是可以直接跳的。

什么意思呢?如果 \(y-x<z-y\),那么左边一直往右跳,跳两次就相当于左边两个点往右平移一个 \(y-x\)

那么直接算一开始中间那个点能平移几次就行,肯定是 \(\frac{z-y-1}{y-x}\)(因为不能跳到相等)。

所以说,一个点向上爬 \(cnt\) 次的代码就出来了(我认为这是核心代码)。

//Group为我设的状态组
Group Move(Group x,int cnt,int &ans){
	while(cnt){
		int d1=x.y-x.x,d2=x.z-x.y,fac;
		if(d1==d2) return x;
		else if(d1>d2) fac=min((d1-1)/d2,cnt),x=(Group){x.x,x.y-fac*d2,x.z-fac*d2};
		else fac=min((d2-1)/d1,cnt),x=(Group){x.x+fac*d1,x.y+fac*d1,x.z};
		cnt-=fac;
		ans+=fac;
	}
	return x;
}

\(LCA\) 时,先跳到同一高度,然后二分一起跳的距离即可。

\(Code:\)

#include<bits/stdc++.h>
#define int long long
#define lowbit(x) x&-x
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
const int inf=0x3f3f3f3f;
struct Group{
	int x,y,z;
	void Sort(){
		if(x>y) swap(x,y);
		if(y>z) swap(y,z);
		if(x>y) swap(x,y);
		return ;
	}
}a,b;

Group Move(Group x,int cnt,int &ans){
	while(cnt){
		int d1=x.y-x.x,d2=x.z-x.y,fac;
		if(d1==d2) return x;
		else if(d1>d2) fac=min((d1-1)/d2,cnt),x=(Group){x.x,x.y-fac*d2,x.z-fac*d2};
		else fac=min((d2-1)/d1,cnt),x=(Group){x.x+fac*d1,x.y+fac*d1,x.z};
		cnt-=fac;
		ans+=fac;
	}
	return x;
}
bool cmp(Group x,Group y){
	if(x.x==y.x&&x.y==y.y&&x.z==y.z) return true;
	return false;
}
signed main()
{
	//freopen("data.in","r",stdin);
	//freopen("a.out","w",stdout); 
	scanf("%lld%lld%lld",&a.x,&a.y,&a.z);
	scanf("%lld%lld%lld",&b.x,&b.y,&b.z);
	a.Sort();b.Sort();
	int da=0,db=0,tmp=0;
	Group fa=Move(a,inf,da),fb=Move(b,inf,db);
	//cout<<da<<" "<<db<<endl;
	if(cmp(fa,fb)){
		printf("YES\n");
		if(da<db) swap(a,b),swap(da,db);
		a=Move(a,da-db,tmp);
		//cout<<a.x<<" "<<a.y<<" "<<a.z<<endl;
		int l=0,r=db;
		while(l<r){
			int mid=l+r>>1;
			if(cmp(Move(a,mid,tmp),Move(b,mid,tmp))) r=mid;
			else l=mid+1;
		}
		printf("%lld",da-db+2*l);
	}
	else printf("NO");
	return 0;
}
posted @ 2023-12-08 20:31  HEIMOFA  阅读(249)  评论(0)    收藏  举报