跳跳棋——二分+建模LCA

题目描述

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

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

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

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

思路

考虑一种类似于二叉树的结构,三个跳棋无法再跳的时候(即$|XY|=|YZ|$时),我把将它视为根,我们把初末状态视为$a,b$两个节点,那么其实就是找$a,b的LCA$,我们先找到$a,b$的根,如果根不同,没有解,否则我们先把$a,b$提到同一深度,记深度差为$x$,然后二分往上跳的高度,记为$l$,答案就是$x+l*2$

code

#include<bits/stdc++.h>
#define I inline
#define dist(x,y) (abs(x-y))
using namespace std;
const int inf=1e9+10;
struct node
{
    int x[5];
    int dep;
}a,b;
int depth;
int ans;

bool operator == (node a,node b)
{
    sort(a.x+1,a.x+4);sort(b.x+1,b.x+4);
    for(int i=1;i<=3;i++)if(a.x[i]!=b.x[i])return 0;
    return 1;
}

I node calc(node a,int k)
{
    int x=a.x[1],y=a.x[2],z=a.x[3];
    node res;
    for(int i=1;i<=3;i++)res.x[i]=a.x[i];
    int d1=dist(x,y),d2=dist(y,z);
    if(d1==d2)return res;
    if(d1<d2)
    {
        int t=min(k,(d2-1)/d1);
        x+=d1*t;y+=d1*t;
        k-=t;depth+=t;
    }
    else
    {
        int t=min(k,(d1-1)/d2);
        y-=d2*t;z-=d2*t;
        k-=t;depth+=t;
    }
    res.x[1]=x;res.x[2]=y;res.x[3]=z;
    if(k)return calc(res,k);
    return res;
}

int main()
{
    cin>>a.x[1]>>a.x[2]>>a.x[3]>>b.x[1]>>b.x[2]>>b.x[3];
    sort(a.x+1,a.x+4);sort(b.x+1,b.x+4);
    node rt1=calc(a,inf);a.dep=depth;depth=0;
    node rt2=calc(b,inf);b.dep=depth;depth=0;
    if(!(rt1==rt2)){cout<<"NO";return 0;}
    if(a.dep<b.dep)swap(a,b);
    ans=a.dep-b.dep;
    a=calc(a,ans);
    int l=0,r=b.dep;
    while(l<r-1)
    {
        int mid=l+r>>1;
        if(calc(a,mid)==calc(b,mid))r=mid;
        else l=mid;
    }
    while(!(calc(a,l)==calc(b,l)))l++;
    puts("YES");
    cout<<ans+l*2;
    
}
原文地址:https://www.cnblogs.com/THRANDUil/p/11792715.html