一个重要的方面是可能的位置数量相当少:
420 = Binomial(8,2) * Binomial(6, 2)
结果是无论选择哪种算法,你都必须注意不要多次通过同一个节点,即通过同一个位置。传统上,国际象棋程序在分析棋子数量较少的位置时使用哈希表。在这里,考虑到少数可能的位置,可以选择完美散列,即没有串通。
关于算法:
-
爬山方法提供了一个局部最小值,而不是一个绝对最小值。它不应该在这里回答问题
- DFS(例如,通过回溯实现)在这里似乎不是最好的,因为您冒着深入错误路线的风险
- A* 算法通常非常有效。但是,它依赖于对给定节点的良好启发式估计。在这里,在手动寻找解决方案之后,很明显骑士必须在很短的表面上转很长时间(疯马)。在这种情况下很难找到好的启发式方法
- BFS 通常的缺点是它会生成大量内存,因此会花费大量时间访问这些节点。在这里,由于可能的节点(位置)数量有限并且由于散列,情况并非如此。此外,使用 BFS,一旦我们得到一个解决方案,我们就确定它是最短的一个
因此,我在 C++ 中实现了 BFS。结果是即时提供的。
下面给出16个半步:
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| W | | W | | | | W | | | | W | | | | |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| | | | | | | | | | | B | | W | | B |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| B | | B | | B | W | B | | | W | B | | | W | B |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| B | | | | B | | W | | B | B | W | | B | B | W |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| W | | | | W | | | | W | | | | | | |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| | W | B | | | | B | | | | | | | | W |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| B | | W | | B | W | W | | B | W | W | | B | W | |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| | | | | | | | | | | B | | W | | B |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| B | | W | | B | | | | | | | | | | |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| | W | | | | W | | | | W | B | | | | B | | B | | B |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| W | | B | | | | B | | | | B | | | | B | | | | |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
| | B | | | | B | W | | | | W | | W | | W | | W | | W |
+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+
还有节目:
#include <iostream>
#include <array>
#include <vector>
#include <tuple>
#include <algorithm>
int depl[9][2] = {
{5,7}, {6,8}, {3,7}, {2,8}, {-1,-1},
{0,6}, {1,5}, {0,2}, {1,3}};
struct position {
std::array<int, 2> B;
std::array<int, 2> W;
int hash;
position *up = nullptr;
position (int w0, int w1, int b0, int b1) {
if (w0 > w1) std::swap (w0, w1);
if (b0 > b1) std::swap (b0, b1);
B[0] = b0;
B[1] = b1;
W[0] = w0;
W[1] = w1;
cal_hash();
}
position() {};
void cal_hash () {
hash = 1000*B[0] + 100*B[1] + 10*W[0] + W[1];
}
std::vector<position> gene_mov (int white_black) {
std::vector<position> res;
if (!white_black) { // White
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
int pos = depl[W[i]][j];
bool test = (pos == W[1-i]) || (pos == B[0]) || (pos == B[1]);
if (!test) {
res.push_back (position(pos, W[1-i], B[0], B[1]));
}
}
}
} else { // Black
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
int pos = depl[B[i]][j];
bool test = (pos == B[1-i]) || (pos == W[0]) || (pos == W[1]);
if (!test) {
res.push_back (position(W[0], W[1], pos, B[1-i]));
}
}
}
}
return res;
}
void print (int shift = 0) {
char displ[3][3] = {{' ',' ',' '},
{' ',' ',' '},
{' ',' ',' '}};
displ[1][1] = ' ';
displ[2 - W[0]/3][W[0]%3] = 'W';
displ[2 - W[1]/3][W[1]%3] = 'W';
displ[2 - B[0]/3][B[0]%3] = 'B';
displ[2 - B[1]/3][B[1]%3] = 'B';
Wshift(shift);
std::cout << "+---+---+---+\n";
for (int i = 0; i < 3; ++i) {
Wshift(shift);
for (int j = 0; j < 3; j++) {
std::cout << "| " << displ[i][j] << " ";
}
std::cout << "|\n";
Wshift(shift);
std::cout << "+---+---+---+\n";
}
std::cout << "\n";
}
void Wshift (int shift) {
for (int i = 0; i < shift; ++i)
std::cout << " ";
}
};
std::tuple<bool, int, std::vector<position>> find_moves (position &first, position &end) {
std::vector<position> moves;
std::array<bool, 10000> pos_found = {false};
std::vector<std::vector<position>> dfs;
using pvector = std::vector<position>;
dfs.push_back(pvector(1, first));
int iter = 1;
int white_black = 0;
while (true) {
int n = dfs[iter-1].size();
if (n == 0) return std::make_tuple(false, 0, moves);
dfs.push_back(pvector(0));
for (int i = 0; i < n; ++i) {
auto candidates = dfs[iter-1][i].gene_mov(white_black);
for (auto &c: candidates) {
if (pos_found[c.hash]) continue;
c.up = &dfs[iter-1][i];
if (c.hash == end.hash) { // Last position attained: get the previous moves
moves.resize(iter+1);
moves[iter] = c;
for (int i = iter - 1; i >= 0; i--) {
moves[i] = *(moves[i+1].up);
}
return std::make_tuple(true, iter, moves);
}
pos_found[c.hash] = true;
dfs[iter].push_back (c);
}
}
iter++;
white_black = 1 - white_black;
}
return std::make_tuple(false, -1, moves);
}
int main () {
position first(6, 8, 0, 2);
position end(0, 2, 6, 8);
bool success;
int n_iter;
std::vector<position> moves;
std::tie (success, n_iter, moves) = find_moves (first, end);
std::cout << "success = " << success << "\n";
std::cout << "n_iter = " << n_iter << "\n";
for (auto &m: moves) {
m.print();
std::cout << "\n";
}
return 0;
}