/*
騎士走棋盤
說明:
騎士旅遊Knight tour在十八世紀初倍受數學家與拼圖迷的注意,它什麼時候被提出已不可考,騎士的走法爲西洋
棋的走法,騎士可以由任一個位置出發,它要如何走完所有的位置。
解法:
騎士的走法,基本上可以用遞回來解決,但是純粹的遞迴在維度大時相當沒有效率,一個聰明的解法由J.CWarnsdorff
在1823年提出, 簡單地說,先將最難的位置走完,接下來的路就寬廣了,騎士所想要的下一步,爲下一不再 選
擇時,所能走的步數最少的一步。使用這個方法,在不使用遞迴的情況下,可以有較高的機率找出走法(找不到走
的機率也是有的)
*/
#include <stdio.h>
int pos[8][8] = {0};
int travel(int, int);
int main()
{
int i, j, startX, startY;
while(1)
{
printf("輸入起始點:");
scanf("%d%d", &startX, &startY);
if(travel(startX, startY)) {
printf("遊歷完成!\n");
}else {
printf("遊歷失敗!\n");
}
for(i=0; i<8; i++) {
for(j=0; j<8; j++) {
printf("%2d ", pos[i][j]);
}
printf("\n");
}
printf("\n");
}
return 0;
}
int travel(int x, int y) {
int i, j, k, l, m;
int tmpX, tmpY;
int count, min, tmp;
//騎士可走的八個方向(順時針)
int ktmoveX[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int ktmoveY[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
//測試下一步座標
int nextX[8] = {0};
int nextY[8] = {0};
//記錄每個方向的出路的個數
int exists[8] = {0};
//起始用1標記位置
i = x;
j = y;
pos[i][j] = 1;
//遍歷棋盤
for(m=2; m<=64; m++) {
//初始化八個方向出口個數
for(l=0; l<8; l++) {
exists[l] = 0;
}
l = 0; //計算可走方向
//試探八個方向
for(k=0; k<8; k++) {
tmpX = i + ktmoveX[k];
tmpY = j + ktmoveY[k];
//邊界 跳過
if(tmpX<0 || tmpY<0 || tmpX>7 || tmpY>7) {
continue;
}
//可走 記錄
if(pos[tmpX][tmpY] == 0) {
nextX[l] = tmpX;
nextY[l] = tmpY;
l++; //可走方向加1
}
}
count = l;
//無路可走 返回
if(count == 0) {
return 0;
//一個方向可走 標記
}else if(count == 1) {
min = 0;
//找出下個位置出路個數
}else {
for(l=0; l<count; l++) {
for(k=0; k<8; k++) {
tmpX = nextX[l] + ktmoveX[k];
tmpY = nextY[l] + ktmoveY[k];
if(tmpX<0 || tmpY<0 || tmpX>7 || tmpY>7) {
continue;
}
if(pos[tmpX][tmpY] == 0) {
exists[l]++;
}
}
}
//找出下個位置出路最少的方向 找最難的路 纔會擴展接下來的路
min = 0;
tmp = exists[0];
for(l=0; l<count; l++) {
if(exists[l] < tmp) {
tmp = exists[l];
min = l;
}
}
}
//用序號標記走過的位置
i = nextX[min];
j = nextY[min];
pos[i][j] = m;//position[i][j]就是 下一步要走的地方
}
return 1;
}