1. 程式人生 > >跳跳棋bzoj2144國家集訓隊

跳跳棋bzoj2144國家集訓隊

多少 std 沒有 OS 擴大 位置 相同 就會 代碼

題目描述

跳跳棋是在一條數軸上進行的。棋子只能擺在整點上。每個點不能擺超過一個棋子。

技術分享圖片

我們用跳跳棋來做一個簡單的遊戲:棋盤上有3顆棋子,分別在a,b,c這三個位置。我們要通過最少的跳動把他們的位置移動成x,y,z。(棋子是沒有區別的)

跳動的規則很簡單,任意選一顆棋子,對一顆中軸棋子跳動。跳動後兩顆棋子距離不變。一次只允許跳過1顆棋子。

寫一個程序,首先判斷是否可以完成任務。如果可以,輸出最少需要的跳動次數。

輸入輸出格式

輸入格式:

第一行包含三個整數,表示當前棋子的位置a b c。(互不相同)

第二行包含三個整數,表示目標位置x y z。(互不相同)

輸出格式:

如果無解,輸出一行NO。

如果可以到達,第一行輸出YES,第二行輸出最少步數。

輸入輸出樣例

輸入樣例#1: 復制
1 2 3
0 3 5
輸出樣例#1: 復制
YES
2

說明

20% 輸入整數的絕對值均不超過10

40% 輸入整數的絕對值均不超過10000

100% 絕對值不超過10^9

分析

這個題目是真的蛇皮,沒話講。

本題是一道LCA加二分的題目,以我的智商是看不出來的。

下面就來說一下我在SAC大佬的幫助下怎麽分析的吧。

設最左邊的棋子為a,中間棋子為b,最右邊的棋子為c。

顯而易見只有三種跳躍方式。

1.b往左邊跳。

2.b往右邊跳。

3.離b近的往裏跳(離b遠的不可以跳,因為會越過兩個棋子)。

當a,c距離和b一樣時,就把當前狀態設為a,b,c的起始狀態,而且a,b,c的起始狀態只有一種。

所以判斷一下a,b,c起始狀態和不和x,y,z的起始狀態一不一樣就行了。

怎麽判斷呢?

顯然,兩個棋子往中間跳才可以回到起始狀態。

但是,如果純模擬的話就又會超時。

比如1,2,100000000。

這樣就會進行很多次操作。

此時,設d1=b-a,d2=c-b。

d1小於d2時我們移動a,然後會發現d1沒變,d2減小了d1所以我們可以連續走d2/d1次,反之亦然,此時d2小於d1了換個方向走。註意:d2%d1等於0時走d2/d1-1步就到根了。

然後怎麽判斷要走多少步呢?

枚舉顯然不行。

那就二分吧。

如果當前二分得到的mid可以使他們狀態相同。

縮小上界,否則擴大下界,下面就貼上代碼了(自認為寫的很清楚)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
void sot(ll &a,ll &b,ll &c)
{
    if(a>b)
    swap(a,b);
    if(a>c)
    swap(a,c);
    if(b>c)
    swap(b,c);
}
ll min(ll x,ll y)
{
    if(x>y)
    return y;
    return x;
}
ll getfa(ll a,ll b,ll c,ll &dep,ll &d)
{
    ll d1=b-a,d2=c-b;
    while(d1!=d2)
    {
        if(d1<d2)
        {
            ll sum=d2/d1,t=d2%d1;
            if(t==0)
            {
                dep+=(sum-1);
                d=d1;
                return a+(sum-1)*d1;
            }
            else
            {
                dep+=sum;
                d2=t;
                a+=(sum)*d1;
                b+=(sum)*d1;

            }
        }
        else
        {
            ll sum=d1/d2,t=d1%d2;
            if(t==0)
            {
                dep+=(sum-1);
                d=d2;
                return a;
            }
            else
            {
                dep+=sum;
                d1=t;
                b-=(sum)*d2;
                c-=(sum)*d2;
            }
        }
    }
    dep=0;
    d=d1;
    return a;
}
void fa(ll &a,ll &b,ll &c,ll step)
{
    ll d1=b-a,d2=c-b;
    while(step>0)
    {
        if(d1<d2)
        {
            ll sum=d2/d1,t=d2%d1;
            if(sum>=step)
            {
                a+=step*d1;
                b+=step*d1;
                if(b==c) b=a,a-=d1;
                return;
            }
            else
            {
                step-=sum;
                b=c-t;
                a=b-d1;
                d2=t;
            }
        }
        else
        {
            ll sum=d1/d2,t=d1%d2;
            if(sum>=step)
            {
                b-=step*d2;
                c-=step*d2;
                if(a==b) b=c,c+=d2;
                return;
            }
            else
            {
                step-=sum;
                b=a+t;
                c=b+d2;
                d1=t;
            }
        }
    }
}
int main()
{
    ll a,b,c,x,y,z,dep1=0,dep2=0,len=0,k=0,kk=0;
    cin>>a>>b>>c>>x>>y>>z;
    sot(a,b,c);
    sot(x,y,z);
    ll kzj=getfa(a,b,c,dep1,k);
    ll pwq=getfa(x,y,z,dep2,kk);
    if(k!=kk||kzj!=pwq) {cout<<"NO";return 0;}
    else
    {
        if(dep1<dep2)
        {
            len+=dep2-dep1;
            fa(x,y,z,len);
        }
        else
        {
            len+=dep1-dep2;
            fa(a,b,c,len);
        }
    }
    ll l=0,r=min(dep1,dep2),ans=0;
    while(l<=r)
    {
        ll mid=(l+r)/2;
        ll a1=a,b1=b,c1=c,x1=x,y1=y,z1=z;
        fa(a1,b1,c1,mid);
        fa(x1,y1,z1,mid);
        if(a1==x1&&b1==y1&&c1==z1)
        r=mid-1,ans=mid;
        else
        l=mid+1;
    }
    cout<<"YES"<<endl;
    cout<<ans*2+len;
}

跳跳棋bzoj2144國家集訓隊