1. 程式人生 > >洛谷 P1852 [國家集訓隊] 跳跳棋

洛谷 P1852 [國家集訓隊] 跳跳棋

cdn roo 區別 完成 sin include fine urn line

題目描述

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

技術分享圖片

我們用跳跳棋來做一個簡單的遊戲:棋盤上有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

非常有趣的一道題目。

可以發現只有唯一的一種方案向裏跳,並且一直這樣跳最後總會達到一種再也不能向裏跳的狀態。

於是我們就可以把向裏跳看成在樹上向父親走,而最後b-a = c-b 的狀態就是根節點。

首先如果兩個狀態的樹根不一樣,無解;否則就是一個求LCA的問題了,直接倍增即可。

/*
    向前跳:  a[1] -= a[2]  (須滿足 a[1] > a[2])
	向後跳: a[2] -= a[1] , a[0] += a[1] (須滿足 a[1] < a[2])
	a[1] == a[2] 就不能跳了 
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;

struct node{
    int x,y,z;
    bool operator !=(const node &u)const{
    	return x!=u.x||y!=u.y||z!=u.z;
	}
}A,B;
	
int a[3],b[3];
unsigned int ans;

node getroot(node x){
	if(x.y==x.z) return x;
	
	if(x.y>x.z) return getroot((node){x.x,(x.y-1)%x.z+1,x.z});
	
	else{
		int o=(x.z-1)/x.y;
		x.x+=o*x.y,x.z-=o*x.y;
		return getroot(x);
	}
}

int getdeep(node x){
	if(x.y==x.z) return 0;
	
	if(x.y>x.z){
		int o=(x.y-1)/x.z;
		x.y-=o*x.z;
		return getdeep(x)+o;
	}
	
	else{
		int o=(x.z-1)/x.y;
		x.x+=o*x.y,x.z-=o*x.y;
		return getdeep(x)+o;
	}	
}

node getnode(node x,int lef){
	if(x.y==x.z) return x;
	
	if(x.y>x.z){
		int o=(x.y-1)/x.z;
		
		if(o>=lef) return (node){x.x,x.y-lef*x.z,x.z};
		
		x.y-=o*x.z;
		return getnode(x,lef-o);
	}
	
	else{
		int o=(x.z-1)/x.y;
		
		if(o>=lef) return (node){x.x+lef*x.y,x.y,x.z-lef*x.y};
		
		x.x+=o*x.y,x.z-=o*x.y;
		return getnode(x,lef-o);
	}		
}

inline void solve(){
	int da=getdeep(A),db=getdeep(B);
	if(da<db) swap(da,db),swap(A,B);
	if(da>db) A=getnode(A,da-db);
	
	ans=da-db;
	
	if(!(A!=B)) return;
	
	for(int i=log(db)/log(2)+1;i>=0;i--) if((1<<i)<=db){
		node AA=getnode(A,1<<i);
		node BB=getnode(B,1<<i);
		if(AA!=BB) A=AA,B=BB,ans+=1<<(i+1);
	}
	
	ans+=2;
}

int main(){
	for(int i=0;i<3;i++) scanf("%d",a+i);
	for(int i=0;i<3;i++) scanf("%d",b+i);
	sort(a,a+3),sort(b,b+3);
	
	for(int i=2;i;i--) a[i]-=a[i-1],b[i]-=b[i-1];
	
	A=(node){a[0],a[1],a[2]},B=(node){b[0],b[1],b[2]};
	if(getroot(A)!=getroot(B)){ puts("NO"); return 0;}
	
	solve();
	
	puts("YES"); 
	printf("%u\n",ans);
	
	return 0;
}

  

洛谷 P1852 [國家集訓隊] 跳跳棋