P1852跳跳棋 题解
好妙啊!这道题让我大开眼界!
写一篇题解加深一下印象。
思路
我重点对把原问题建模成一个树上问题的部分进行补充。
最开始读题的时候没有看到最多只跳过一个棋子,没想到这竟然是突破口。
我们假设一个状态 \((x, y, z)\),不妨设 \(x \le y \le z\)。
考虑它能怎么变化?这里为了方便描述,在引入两个变量 \(d1,d2\) 表示 \(x\) 和 \(z\) 与 \(y\) 之间的距离。
它会有以下几种变换方式:
-
\(y\) 向左跳,\(y \to y - 2 \times d1\),\((x, y, z) \to (y - 2 \times d1, x, z)\)
-
\(y\) 向右跳,\(y \to y + 2 \times d2\), \((x, y, z) \to (x, z, y + 2 \times d2)\)
-
\(x\) 向右(中间)跳, \(x \to x + 2 \times d1\), \((x, y, z) \to (y, x + 2 \times d1, z)\)
-
\(z\) 向左(中间)跳, \(z \to z - 2 \times d2\),\((x, y, z) \to (x, z - 2 \times d2)\)
可以发现由于最多只跳过一个棋子,\(3\),\(4\) 操作必然矛盾。
换句话说,假如只进行 \(3\),\(4\) 操作操作将是唯一的(会操作小的那边)!(\震惊)
这里有一个小发现: \(1,2\) 操作和 \(3,4\) 操作是互逆的:
比如 \((1, 2, 9)\) 进行 \(3\) 操作 变成 \((2, 3, 9)\);\((2, 3, 9)\) 进行 \(1\) 操作 又变回 \((1, 2, 9)\)。
思考继续深入。每次 \((x, y, z)\) 的范围都会减少 \(\min(d1, d2)\)。
范围会在某个状态重新变大吗或无法变化吗?
答案是后者。我们刚才忘记讨论 \(d1 = d2\) 的情况了!假如 \(d1 = d2\) 那就无法操作了。
我们假设这样的状态为 \((x', y', z')\)。
从 \((x', y', z')\) 到 \((x, y, z)\) 要进行的操作是不确定的 (\(1\),\(2\) 其中一种)。
而从 \((x, y, z)\) 到 \((x', y', z')\) 要进行的操作是确定的(\(3\),\(4\) 其中一种)。
并且 \(1, 2\) 和 \(3, 4\) 操作是互逆的。
二叉树不就是这样的吗?走到儿子和回到父亲是互逆的。儿子有多个,不确定;父亲只有一个,确定。
所以从原始状态到目标状态的过程对应的就是树上两点的距离。
设原始状态,目标状态分别为 \(A\), \(B\)。
解决方法就是先找到 LCA ,再计算出 \(A\),\(B\) 到 LCA 的距离(用 \(3,4\) 操作唯一解决)
剩下部分可以参考别的题解,主要的就是我这里的 jump 函数。
#include<bits/stdc++.h>
using namespace std;
struct node{
int x, y, z;
// void print() {
// cout << x << " " << y << " " << z << "\n";
// }
bool operator == (node T) {
return (x == T.x && y == T.y && z == T.z);
}
}A, B;
pair<node, int> get(int x, int y, int z) {
int res = 0;
while(1) {
int d1 = y - x, d2 = z - y;
if(d1 == d2) break;
if(d1 < d2) {
int s = (d2 - 1) / d1;
y = z - d2 + s * d1;
x = y - d1;
res += s;
}
else {
int s = (d1 - 1) / d2;
y = x + d1 - s * d2;
z = y + d2;
res += s;
}
}
return {(node){x, y, z}, res};
}// 跳到根的步数
node jump(int x, int y, int z, int step) {
while(step) {
int d1 = y - x, d2 = z - y;
if(d1 == d2) break;
if(d1 < d2) {
int s = min(step, (d2 - 1) / d1);
y = z - d2 + s * d1;
x = y - d1;
step -= s;
}
else {
int s = min(step, (d1 - 1) / d2);
y = x + d1 - s * d2;
z = y + d2;
step -= s;
}
}
return (node){x, y, z};
}// 跳 step 步到的点
void init(node &P) {
int a, b, c;
cin >> a >> b >> c;
P.x = min({a, b, c});
P.z = max({a, b, c});
P.y = a + b + c - P.x - P.z;
}
int main() {
init(A), init(B);
auto tmp1 = get(A.x, A.y, A.z);
auto tmp2 = get(B.x, B.y, B.z);
if(!(tmp1.first == tmp2.first)) {
cout << "NO";
return 0;
}
cout << "YES\n";
int tot1 = tmp1.second, tot2 = tmp2.second;
if(tot1 < tot2) swap(A, B), swap(tot1, tot2);
A = jump(A.x, A.y, A.z, tot1 - tot2);
int l = 0, r = 1e9, ans = 0;
while(l <= r) {
int mid = (l + r) / 2;
if(jump(A.x, A.y, A.z, mid) == jump(B.x, B.y, B.z, mid)) {
ans = mid, r = mid - 1;
}
else l = mid + 1;
}
cout << ans * 2 + tot1 - tot2;
return 0;
}

浙公网安备 33010602011771号