2025-02-20 18:49阅读: 4评论: 0推荐: 0

P1852跳跳棋 题解

好妙啊!这道题让我大开眼界!
写一篇题解加深一下印象。

思路

我重点对把原问题建模成一个树上问题的部分进行补充。

最开始读题的时候没有看到最多只跳过一个棋子,没想到这竟然是突破口。

我们假设一个状态 (x,y,z),不妨设 xyz

考虑它能怎么变化?这里为了方便描述,在引入两个变量 d1,d2 表示 xzy 之间的距离。

它会有以下几种变换方式:

  1. y 向左跳,yy2×d1(x,y,z)(y2×d1,x,z)

  2. y 向右跳,yy+2×d2(x,y,z)(x,z,y+2×d2)

  3. x 向右(中间)跳, xx+2×d1(x,y,z)(y,x+2×d1,z)

  4. z 向左(中间)跳, zz2×d2(x,y,z)(x,z2×d2)

可以发现由于最多只跳过一个棋子34 操作必然矛盾。
换句话说,假如只进行 34 操作操作将是唯一的(会操作小的那边)!(\震惊)

这里有一个小发现: 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) 要进行的操作是不确定的 (12 其中一种)。

而从 (x,y,z)(x,y,z) 要进行的操作是确定的(34 其中一种)。

并且 1,23,4 操作是互逆的。

二叉树不就是这样的吗?走到儿子和回到父亲是互逆的。儿子有多个,不确定;父亲只有一个,确定。

所以从原始状态到目标状态的过程对应的就是树上两点的距离。

设原始状态,目标状态分别为 AB

解决方法就是先找到 LCA ,再计算出 ABLCA 的距离(用 34 操作唯一解决)

剩下部分可以参考别的题解,主要的就是我这里的 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;
}

本文作者:merlinkkk

本文链接:https://www.cnblogs.com/merlinkkk/p/18727276

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   merlinkkk  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开