HDU - 3830 Checkers (LCA + 二分)

HDU - 3830  Checkers VJ地址

题意:

给你一组三个互不相同的点,按照规定能不能转移到第二组的点,如果能算出最少的步数。

一个点只能移到相邻的点的对称位置,比如2 的的相邻点是 5,它可以移到8。但是这之间存在第三个点就不能移动,比如存在6, 7,就2就不能移动到8。

那么2 5 -> 5 8可以看做2移动了3个位置,5移动三个位置。

对于三个数 x, y, z一般可以有2或者三种移动方式

  1. y-x = z-y 的时候,只能 y 往两边移动,只有两种移动方式。
  2. y-x != z-y 的时候,可以移动和y距离的的那个数,往中间移动 + y往两边移动,就有三种移动方式

然后可以看做一棵二叉树,两个度的为根,三个度的为除了根以外的节点。

如果两组节点可以互相转换的要求为根节点相同,如果根节点相同,主要是找LCA。

如果一一模拟转移的话1 2 100 - >98 99 100需要很多次,可以用辗转相除的方式。

1 2 100 -> 2 3 100需要一步, 1 2 100 -> 3 4 100需要两步,1 2 100 - 98 99 100就可以一步算出来。

然后二分查找LCA就解决了。

AC代码

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e5+10;
struct Node{
    int x[4];
}node[3], root[3];

int get_root(Node &a){    //查找根节点,返回深度
    int l = a.x[2] - a.x[1], r = a.x[3] - a.x[2];
    int d = 0;
    while(l != r){
        if(l > r){
            d += l/r;
            a.x[3] -= l/r*r;
            a.x[2] -= l/r*r;
            l %= r;
        }
        else if(l < r){
            d += r/l;
            a.x[1] += r/l*l;
            a.x[2] += r/l*l;
            r %= l;
        }
        if(l == 0){
            a.x[2] += r;
            a.x[3] += r;
            l += r;
            d--;
        }
        if(r == 0){
            a.x[2] -= l;
            a.x[1] -= l;
            r += l;
            d--;
        }
    }
    return d;
}



int Equal(Node a, Node b){    //判断是否相等
    int flag = 1;
    for(int i = 1; i <= 3; i++){
        if(a.x[i] != b.x[i])
            flag = 0;
    }
    return flag;
}

Node change(Node a, int num){    //把a在树上向上移num次
    int l = a.x[2] - a.x[1], r = a.x[3] - a.x[2];
    while(num != 0){
        if(l < r){
            if(num > r/l){
                a.x[1] += r/l*l;
                a.x[2] += r/l*l;
                num -= r/l;
                r %= l;
            }
            else{
                a.x[1] += l*num;
                a.x[2] += l*num;
                num = 0;
            }
        }
        else{
            if(num > l/r){
                a.x[2] -= l/r*r;
                a.x[3] -= l/r*r;
                num -= l/r;
                l %= r;
            }
            else{
                a.x[2] -= num*r;
                a.x[3] -= num*r;
                num = 0;
            }
        }
    }
    return a;
}

int solve(Node a, Node b, int h){    //把a和b向上移h并判断是否相等
    a = change(a, h);
    b = change(b, h);
    return Equal(a, b);
}


int lca(int deep_a, int deep_b){    //找到LCA节点,返回步数
    int l , r, num = 0, m, ans = 0;
    Node u = node[1], v = node[2];
    if(deep_a < deep_b){
        swap(u, v);
    }
    num = abs(deep_a - deep_b);
    u = change(u, num);

    if(!Equal(u, v)){
        l = 1; r = min(deep_a, deep_b);
        ans = r;
        while(l <= r){
            m = l+r>>1;
            if(solve(u, v, m)){
                ans = m;
                r = m-1;
            }
            else
                l = m+1;
        }
    }
}

int main(){
    while(scanf("%d%d%d", &node[1].x[1], &node[1].x[2], &node[1].x[3]) != EOF){
        sort(node[1].x+1, node[1].x+4);
        root[1] = node[1];
        for(int i = 2; i <= 2; i++){
            for(int j = 1; j <= 3; j++){
                scanf("%d", &node[i].x[j]);
            }
            sort(node[i].x+1, node[i].x+4);
            root[i] = node[i];
        }
        int a, b, flag = 0;
        a = get_root(root[1]);
        b = get_root(root[2]);
        for(int i = 1; i <= 3; i++){
            if(root[1].x[i] != root[2].x[i])
                flag = 1;
        }
        if(flag){
            printf("NO\n");
            continue;
        }
        printf("YES\n%d\n", lca(a, b));

    }
    return 0;
}

/*
1 3 4
0 3 5
-2 2 8
0 2 6
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值