BZOJ2144 【國家集訓隊】跳跳棋(建模 + 二分 + LCA)
阿新 • • 發佈:2018-10-31
任重而道遠
跳跳棋是在一條數軸上進行的。棋子只能擺在整點上。每個點不能擺超過一個棋子。我們用跳跳棋來做一個簡單的
遊戲:棋盤上有3顆棋子,分別在a,b,c這三個位置。我們要通過最少的跳動把他們的位置移動成x,y,z。(棋
子是沒有區別的)跳動的規則很簡單,任意選一顆棋子,對一顆中軸棋子跳動。跳動後兩顆棋子距離不變。一次只
允許跳過1顆棋子。
寫一個程式,首先判斷是否可以完成任務。如果可以,輸出最少需要的跳動次數。
Input
第一行包含三個整數,表示當前棋子的位置a b c。(互不相同)
第二行包含三個整數,表示目標位置x y z。(互不相同)
Output
如果無解,輸出一行NO。如果可以到達,第一行輸出YES,第二行輸出最少步數。
Sample Input
1 2 3
0 3 5
Sample Output
YES
2
【範圍】 100% 絕對值不超過10^9
AC程式碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int oo = 1e9; struct Node { int a, b, c; bool operator != (const Node &nd) const { return a != nd.a || b != nd.b || c != nd.c; } }u, v; int tot, dep1, dep2, a[5]; Node cal (Node nd, int k) { int d1 = nd.b - nd.a, d2 = nd.c - nd.b; if (d1 == d2) return nd; if (d1 < d2) { int x = min (k, (d2 - 1) / d1); k -= x, tot += x; nd.a += x * d1, nd.b += x * d1; } else { int x = min (k, (d1 - 1) / d2); k -= x, tot += x; nd.c -= x * d2, nd.b -= x * d2; } if (k) return cal (nd, k); else return nd; } int main () { scanf ("%d%d%d", &a[1], &a[2], &a[3]); sort (a + 1, a + 4); u = (Node) {a[1], a[2], a[3]}; scanf ("%d%d%d", &a[1], &a[2], &a[3]); sort (a + 1, a + 4); v = (Node) {a[1], a[2], a[3]}; Node rt1 = cal (u, oo); dep1 = tot, tot = 0; Node rt2 = cal (v, oo); dep2 = tot, tot = 0; if (rt1 != rt2) { puts ("NO"); return 0; } else puts ("YES"); if (dep1 < dep2) swap (dep1, dep2), swap (u, v); int delta = dep1 - dep2; u = cal (u, delta); int l = 0, r = dep2; while (l <= r) { int mid = l + r >> 1; if (cal (u, mid) != cal (v, mid)) l = mid + 1; else r = mid - 1; } printf ("%d", delta + l * 2); return 0; }