题意:给你一个8*8的棋盘,已知骑士的开始位置、结束位置,让你求得骑士从开始位置走到结束位置所需要的最小步数是多少(骑士走日字)?
输入:输入包括多组数据,每一行都是一组开始位置和结束位置,位置由两个字符组成,一个是小写字母(a-h),一个是数字(1-8),起始位置结束位置由一个空格隔开。
纯数学方法:
首先,对于两个点,现在设横纵坐标的差值分别为x、y,由于骑士走的方向有8种,而实际上只会出现4种(举个例子,本来方向向量有(1,2),(2,1),(1,-2),(2,-1),(-1,2),(-2,1),(-1,-2),(-2,-1),但是如果要求最小的步数,就不可能同时出现(1,2)和(-1,-2),依次类推)。
设方向向量为(1,2),(2,1),(2,-1),(1,-2)的分别有a,b,c,d次,其中a,b,c,d可以为负数,a为负数代表方向向量为(-1,-2),
于是可以列两个方程: a+2b+2c+d=x
2a+b-c-2d=y
我们所要求的就是|a|+|b|+|c|+|d|的最小值。
首先把a,b,看做常量,解得:c=(-4a-5b+2x+y)/3
d=(5a+4b-x-2y)/3
前面我们得出方程 a+2b+2c+d=x , 2a+b-c-2d=y
所以2x+y = 2(a+2b+2c+d)+(2a+b-c-2d)
= 4a+5b+3c
(2x+y) mod 3 = (4a+5b+3c) mod 3
= (a+2b) mod 3
那么有2x+y和a+2b模3同余。
现在2x+y已知,对b进行枚举,对每个知道a模3是多少,进而再对可能的a进行枚举,从而解出c、d,进而求出步数。
但是如果上面所提到的就是你能想到的全部,还是会出错。。
特别需要注意的一点就是角落的问题,比如输入a1、b2按上面的方法算的是2,实际上是4。
对于8*8的棋盘只有四种特殊情况就是:
a1、b2, a8、b7, g2、h1, g7、h8
对这四种情况单独拿出来就好了。。
举一例:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
int main()
{
int a, b, c, d, x, y, m, ans;
string s1, s2;
while(cin >> s1 >> s2)
{
if ((s1=="a1" && s2=="b2") || (s1=="b2" && s2=="a1") || (s1=="g2" && s2=="h1") || (s1=="h1" && s2=="g2"))
{
cout << "To get from " << s1 << " to " << s2 << " takes 4 knight moves." << endl;
continue;
}
if ((s1=="a8" && s2=="b7") || (s1=="b7" && s2=="a8") || (s1=="g7" && s2=="h8") || (s1=="h8" && s2=="g7"))
{
cout << "To get from " << s1 << " to " << s2 << " takes 4 knight moves." << endl;
continue;
}
x = s2[0] - s1[0];
y = s2[1] - s1[1];
ans = 10001;
for(b = -8; b <= 8; b++) //对b进行枚举
{
m = y + 2 * x - 2 * b + 30;
m %= 3; //a模3的余数
for(a = m - 9; a <= m + 9; a += 3)
{
c = (2 * x + y - 4 * a - 5 * b) / 3;
d = (5 * a + 4 * b - x - 2 * y) / 3;
if(ans > abs(a) + abs(b) + abs(c) + abs(d))
ans = abs(a) + abs(b) + abs(c) + abs(d);
}
}
cout << "To get from " << s1 << " to " << s2 << " takes " << ans << " knight moves." << endl;
}
return 0;
}