hdu3830Checkers

Checkers

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 125536/65536K (Java/Other)
Total Submission(s) : 3   Accepted Submission(s) : 1
Problem Description
Little X, Little Y and Little Z are playing checkers when Little Y is annoyed. So he wants to make the chessboard much bigger. Although Little Z insists the original version, Little X stands by Little Y. After they enlarge the chessboard, the chessboard turns to an infinite line.
The chessboard is like the Number Axes now, with each integer point able to hold a checker. At initial status there are three checkers on three different integer points , and through the game there always are three checkers. Every time, they can choose a checker A to jump across a pivot checker B to a new position(but the distance between old A and B equals to new A and B, and there should be no other checkers except B in the range [old A, new A]).
After playing for a while, they wonder whether an given status a,b,c can be transferred to x,y,z. obeying the rules. Since the checkers are considered the same, it is unnecessary for a must jump to x.
 

Input
The first line is a,b,c. The second line is x,y,z. They are all integers in range (-10^9, 10^9) and the two status are valid.
 

Output
The first line is YES or NO, showing whether the transfer can be achieved. If it is YES, then output the least steps in the second line.
 

Sample Input
 
 
1 2 30 3 5
 
Sample Output
 
 
YES2[hint]The middle checker jumps to position 0, and the status is 0 1 3

Then , the middle one jumps to 5.[/hint]

题意:给你三个棋子的初始位置和终止位置,问是否能到达终止位置,如果能输出最少的步数,这三个棋子就像跳棋一样,只

能隔着一个棋子跳。

思路:思路参考了网上,对于两种状态,我们将三个点从小到大排序,设3个点分别为x,y,z,s1=y-x,s2=z-y;接下来

就有三种情况出现:

1、当s1==s2时,下一状态只能是y往x方向跳或者y往y方向跳;

2、当s1>s2时,下一状态可以是y往两方向跳,也可以是z往x和y之间跳;

3、当s2>s1时,下一状态可以是y往两方向跳,也可以是x往z和y之间跳;

对于上述情况,每个点的度只可能是2或3,那么就可以知道状态之间的联系就是二叉树组成的森林。对于第1种状态,相当于
是二叉树的根,后面两种情况是树中非根节点,其余的每个状态,a或c往中间跳表示往父亲节点走一步对于所有状态,中间节

点往左右跳分别对应往左右孩子走一步。这下我们就能知道,开始和中止状态对应树中的两个点,求两个点的距离就是LCA问

题了。判断两个点是否在同一棵树上,只要判断两点的树根是否相同,求距离时,有点类似倍增的想法,先让两点达到同一深

度,然后二分LCA的深度d,LCA的深度肯定是在[0,dep]之间的,那么深度差dis=dep-d;然后看这时的新状态是否一致,

如果不一致,说明深度太深了,那么d应该小一些,如果一致,在继续往深度深的地方找,看是否为最近公共祖先,最后结果

即为dis*2+操作深度de。这里求深度时我么可以用辗转相除法来操作,可以节约大量时间。

代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <cmath>
using namespace std;
struct node
{
    long long int x,y,z;
    long long int dep;
}a,b;
bool check(node a,node b)
{
    if(a.x==b.x&&a.y==b.y&&a.z==b.z)
    {
        return true;
    }
    return false;
}
void sort(node &a)
{
    if(a.x>a.y)swap(a.x,a.y);
    if(a.x>a.z)swap(a.x,a.z);
    if(a.y>a.z)swap(a.y,a.z);
}
node findroot(node &a)
{
    node tmp=a;
    tmp.dep=0;
    long long int dep=0;
    while(abs(tmp.y-tmp.x)!=abs(tmp.z-tmp.y))
    {
        long long int len=abs(tmp.x-tmp.y);
        long long int len1=abs(tmp.y-tmp.z);
        if(len1>len)
        {
            long long int c=(len1-1)/len;
            dep+=c;
            tmp.x+=c*len;
            tmp.y+=c*len;
        }
        else
        {
            long long int c=(len-1)/len1;
            dep+=c;
            tmp.y-=c*len1;
            tmp.z-=c*len1;
        }
    }
    a.dep=dep;
    return tmp;
}
void update(node &a,long long int dis)
{
    long long int c=0;
    while(c<dis)
    {
        long long int len=abs(a.x-a.y);
        long long int len1=abs(a.y-a.z);
        long long int k=abs(dis-c);
        if(len1>len)
        {
            long long int z=(len1-1)/len;
            long long int minn=min(z,k);
            a.x+=minn*len;
            a.y+=minn*len;
            c+=minn;
            if(minn==k)break;
        }
        else
        {
            long long int z=(len-1)/len1;
            long long int minn=min(z,k);
            a.y-=minn*len1;
            a.z-=minn*len1;
            c+=minn;
            if(minn==k)break;
        }
    }
    a.dep-=dis;
}
int main()
{
    while(scanf("%lld%lld%lld",&a.x,&a.y,&a.z)!=EOF)
    {
        scanf("%lld%lld%lld",&b.x,&b.y,&b.z);
        sort(a);
        sort(b);
        a.dep=b.dep=0;
        node ra=findroot(a);
        node rb=findroot(b);
        if(!check(ra,rb))
        {
            printf("NO\n");
            continue;
        }
       long long int dis=abs(a.dep-b.dep);
        if(a.dep>b.dep)
        {
            update(a,a.dep-b.dep);
        }
        else update(b,b.dep-a.dep);
       long long  int l=0,r=a.dep;
        while(l<=r)
        {
            long long int mid=(l+r)/2;
            long long int di=a.dep-mid;
            node c,d;
            c=a;
            d=b;
            update(c,di);
            update(d,di);
            if(check(c,d))
            {
                l=mid+1;
            }
            else r=mid-1;
        }
        long long int ans=(a.dep-r)*2+dis;
        printf("YES\n%lld\n",ans);

    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值