题目地址:
https://www.acwing.com/problem/content/description/2137/
在国际象棋和中国象棋中,马的移动规则相同,都是走“日”字,我们将这种移动方式称为马步移动。如下图所示,从标号为
0
0
0的点出发,可以经过一步马步移动达到标号为
1
1
1的点,经过两步马步移动达到标号为
2
2
2的点。
任给平面上的两点
p
p
p和
s
s
s,它们的坐标分别为
(
x
p
,
y
p
)
(x_p,y_p)
(xp,yp)和
(
x
s
,
y
s
)
(x_s,y_s)
(xs,ys),其中,
x
p
,
y
p
,
x
s
,
y
s
x_p,y_p,x_s,y_s
xp,yp,xs,ys均为整数。从
(
x
p
,
y
p
)
(x_p,y_p)
(xp,yp)出发经过一步马步移动可以达到
(
x
p
+
1
,
y
p
+
2
)
,
(
x
p
+
2
,
y
p
+
1
)
,
(
x
p
+
1
,
y
p
−
2
)
,
(
x
p
+
2
,
y
p
−
1
)
,
(
x
p
−
1
,
y
p
+
2
)
,
(
x
p
−
2
,
y
p
+
1
)
,
(
x
p
−
1
,
y
p
−
2
)
,
(
x
p
−
2
,
y
p
−
1
)
(x_p+1,y_p+2),(x_p+2,y_p+1),(x_p+1,y_p−2),(x_p+2,y_p−1),(x_p−1,y_p+2),(x_p−2,y_p+1),(x_p−1,y_p−2),(x_p−2,y_p−1)
(xp+1,yp+2),(xp+2,yp+1),(xp+1,yp−2),(xp+2,yp−1),(xp−1,yp+2),(xp−2,yp+1),(xp−1,yp−2),(xp−2,yp−1)。假设棋盘充分大,并且坐标可以为负数。现在请你求出从点
p
p
p到点
s
s
s至少需要经过多少次马步移动?
输入格式:
只包含
4
4
4个整数,它们彼此用空格隔开,分别为
x
p
,
y
p
,
x
s
,
y
s
x_p,y_p,x_s,y_s
xp,yp,xs,ys。
输出格式:
输出一个整数,表示从点
p
p
p到点
s
s
s至少需要经过的马步移动次数。
数据范围:
−
1
0
7
<
x
p
,
y
p
,
x
s
,
y
s
<
1
0
7
−10^7<x_p,y_p,x_s,y_s<10^7
−107<xp,yp,xs,ys<107
由对称性,步数与从
(
0
,
0
)
(0,0)
(0,0)到
(
x
,
y
)
=
(
∣
x
p
−
x
s
∣
,
∣
y
p
−
y
s
∣
)
(x,y)=(|x_p-x_s|,|y_p-y_s|)
(x,y)=(∣xp−xs∣,∣yp−ys∣)的步数相同,进一步地,若
x
<
y
x<y
x<y,步数与到
(
y
,
x
)
(y,x)
(y,x)也相同。所以问题转化为从
(
0
,
0
)
(0,0)
(0,0)到
(
x
,
y
)
,
x
≥
y
(x,y), x\ge y
(x,y),x≥y的最小步数是多少,设步数为
f
(
x
,
y
)
f(x, y)
f(x,y)。我们可以先考虑步数比较少的情形,试图找出规律。我们只关注第一象限中
x
≥
y
x\ge y
x≥y的区域:
这个区域,又可以进一步细化为
x
≥
2
y
x\ge 2y
x≥2y的区域和
2
y
>
x
≥
y
2y>x\ge y
2y>x≥y的区域,前者我们称为
1
1
1号区域,而对于后者,在图中我们可以发现,当
2
y
>
x
≥
y
2y>x\ge y
2y>x≥y的时候,由于在
x
≥
y
x\ge y
x≥y的区域内,当
x
≥
5
x\ge 5
x≥5的时候,有
f
(
x
,
y
)
=
f
(
x
+
1
,
y
−
1
)
f(x, y)=f(x+1, y-1)
f(x,y)=f(x+1,y−1),所以我们可以从
(
x
,
y
)
(x,y)
(x,y)这个点向右下方走,直到走到
1
1
1号区域为止,所以本质上我们只需要对
1
1
1号区域求解即可。
接下来只考虑
1
1
1号区域。容易发现,当
x
=
2
y
x=2y
x=2y的时候,
f
(
x
,
y
)
=
f
(
2
y
,
y
)
=
y
f(x,y)=f(2y,y)=y
f(x,y)=f(2y,y)=y,并且
f
(
2
y
,
y
−
d
)
=
y
+
d
%
2
f(2y,y-d)=y+d\%2
f(2y,y−d)=y+d%2,即
1
1
1号区域内,当
x
x
x是偶数的时候,有
f
(
x
,
y
)
=
x
/
2
+
(
x
/
2
−
y
)
%
2
f(x,y)=x/2+(x/2-y)\%2
f(x,y)=x/2+(x/2−y)%2;当
x
x
x是奇数的时候,由于
f
(
x
,
y
)
=
f
(
x
+
1
,
y
+
1
)
f(x,y)=f(x+1,y+1)
f(x,y)=f(x+1,y+1),所以有
f
(
x
,
y
)
=
(
x
+
1
)
/
2
+
(
(
x
+
1
)
/
2
−
(
y
+
1
)
)
%
2
f(x,y)=(x+1)/2+((x+1)/2-(y+1))\%2
f(x,y)=(x+1)/2+((x+1)/2−(y+1))%2,可以验证
y
=
0
y=0
y=0的时候公式也成立。此外还有几个特殊情况需要特判,分别是
f
(
0
,
0
)
=
0
,
f
(
1
,
0
)
=
3
,
f
(
2
,
2
)
=
4
f(0,0)=0,f(1, 0)=3, f(2,2)=4
f(0,0)=0,f(1,0)=3,f(2,2)=4。后两个特例的产生原因是路径会经过
x
≥
y
x\ge y
x≥y这个区域之外。
简要证明一下。我们只考虑
x
≥
y
x\ge y
x≥y的区域。首先暴力解出
f
(
x
,
y
)
≤
4
f(x,y)\le 4
f(x,y)≤4的区域,这一步可以BFS来做。
先证明不在
1
1
1号区域的情形,这个可以用数学归纳法来考虑,对
f
(
x
,
y
)
f(x,y)
f(x,y)进行归纳,并且假设最短路径可以完全含在
x
≥
y
x\ge y
x≥y的区域里。对于某个
f
(
x
,
y
)
≥
5
f(x,y)\ge 5
f(x,y)≥5的格子,设
k
=
f
(
x
,
y
)
k=f(x,y)
k=f(x,y),那么由归纳假设,对于最少步数是
k
−
1
k-1
k−1的点是成立上面的命题的,那么对于
(
x
,
y
)
(x,y)
(x,y)这个点,其必然是从某个最少步数是
k
−
1
k-1
k−1的点扩展而来,比如是从
(
a
,
b
)
(a,b)
(a,b)扩展而来,找到最右下角的那个
(
a
,
b
)
(a,b)
(a,b),这样这个
(
a
,
b
)
(a,b)
(a,b)能走到
(
x
+
1
,
y
−
1
)
(x+1,y-1)
(x+1,y−1),所以
f
(
x
+
1
,
y
−
1
)
=
f
(
x
,
y
)
f(x+1,y-1)=f(x,y)
f(x+1,y−1)=f(x,y)。
再证明
1
1
1号区域的情形,容易证明
x
=
2
y
x=2y
x=2y这条直线上,
f
(
x
,
y
)
=
y
f(x,y)=y
f(x,y)=y。先用数学归纳法证明
x
x
x为偶数的情形,可以类似上面的方法证明出
f
(
x
,
y
)
=
f
(
x
,
y
−
2
)
=
f
(
x
,
y
−
4
)
=
.
.
.
f(x,y)=f(x,y-2)=f(x,y-4)=...
f(x,y)=f(x,y−2)=f(x,y−4)=...。接下来我们两列两列考虑,在图中是可以发现规律的,对于
x
x
x是偶数的那一列,从上到下是
f
(
2
y
,
y
)
,
f
(
2
y
,
y
)
+
1
,
f
(
2
y
,
y
)
,
f
(
2
y
,
y
)
+
1
,
.
.
.
f(2y, y), f(2y,y)+1,f(2y, y), f(2y,y)+1,...
f(2y,y),f(2y,y)+1,f(2y,y),f(2y,y)+1,...,对于
x
x
x是奇数的那一列,最上面的数字可以由
f
(
x
,
y
)
=
f
(
x
+
1
,
y
+
1
)
f(x,y)=f(x+1, y+1)
f(x,y)=f(x+1,y+1)定出,然后向下依次是
f
(
2
y
,
y
)
,
f
(
2
y
,
y
)
+
1
,
f
(
2
y
,
y
)
,
f
(
2
y
,
y
)
+
1
,
.
.
.
f(2y, y), f(2y,y)+1,f(2y, y), f(2y,y)+1,...
f(2y,y),f(2y,y)+1,f(2y,y),f(2y,y)+1,...,规律一样。接下来可以用数学归纳法证明这个两列的规律。先证明两列两列所需步数是单调上升的,这一点比较容易,接下来可以按照马的走法模拟一遍,即可证明。
代码如下:
#include <iostream>
using namespace std;
inline long f(long x, long y) {
if (!x && !y) return 0;
if (x == 1 && y == 0) return 3;
if (x == 2 && y == 2) return 4;
if (y <= x - y) {
if (x % 2 == 0) return x / 2 + (x / 2 - y) % 2;
else return (x + 1) / 2 + ((x + 1) / 2 - (y + 1)) % 2;
}
long d = (2 * y - x) / 3;
if (!d) d = 1;
x += d, y -= d;
return f(x, y);
}
int main() {
long x, y, a, b;
scanf("%ld%ld%ld%ld", &x, &y, &a, &b);
long c = abs(x - a), d = abs(y - b);
if (c < d) swap(c, d);
printf("%ld\n", abs(f(c, d)));
return 0;
}
时空复杂度 O ( 1 ) O(1) O(1)。