1. 程式人生 > >馬踏棋盤(騎士周遊問題),深度優先搜尋

馬踏棋盤(騎士周遊問題),深度優先搜尋

馬踏棋盤問題(又稱騎士周遊或騎士漫遊問題是演算法設計的經典問題之一)。問題描述:國際象棋的棋盤是8×8的方格棋盤,先將馬放在任意指定的方格中,按照馬走棋的規則將馬進行移動,要求每個方格只能進入一次,最終使得馬走遍棋盤的64個方格。除了邊緣的位置,馬每次有8種走法。 關於國際象棋馬的走法 雖然遞迴,回溯法效率很低,但是也是一種解決方式。就是按照一條路走到底碰壁了在回來走另一條路。就如上圖所示,馬開始走,有8種選擇,比如現在選擇第一種方式,然後在1的位置又有8種選擇,照著下圖這樣走,如果有哪一次走不通了,就往回退,直到退到初始位置,說明從該位置不能遍歷整個棋盤 在這裡插入圖片描述 程式碼如下

#include <stdio.h>
#include <time.h> //包含該標頭檔案,以便計算程式用時

//棋盤大小
#define X 8
#define Y 8
//用二維陣列表示棋盤
int chess[X][Y];


//列印棋盤,棋盤是全域性變數,這裡就不寫函式引數了
void print()
{
	for (int i = 0; i < Y; i++)
	{
		for (int j = 0; j < X; j++)
		{
			//輸出長度為2,不足左補空格
			printf("%2d  ", chess[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
//x,y表示當前位置,count表示相對於位置(x,y)的8種走法中該走哪一種
int nextxy(int &x, int &y, int count)
{
	//8種選擇是x加兩格,x退兩格與y退一格,y加一格組合,有四種
	//x退一格,x加一格與y加兩格,y退兩格組合,有四種,一共8種
	switch (count)
	{
	case 0:
		//首先位置要有效,其次該位置沒有走過
		if (x + 2 <= X - 1 && y + 1 <= Y - 1 && chess[x + 2][y + 1] == 0)
		{
			//如果有效位置,則將(x,y)改為馬下一次要到的位置
			x += 2;
			y += 1;
			return 1;
		}
		break;
	case 1:
		//首先位置要有效,其次該位置沒有走過
		if (x + 2 <= X - 1 && y -1 >= 0 && chess[x + 2][y - 1] == 0)
		{
			//如果有效位置,則將(x,y)改為馬下一次要到的位置
			x += 2;
			y -= 1;
			return 1;
		}
		break;
	case 2:
		//首先位置要有效,其次該位置沒有走過
		if (x - 2 >= 0 && y - 1 >= 0 && chess[x - 2][y - 1] == 0)
		{
			//如果有效位置,則將(x,y)改為馬下一次要到的位置
			x -= 2;
			y -= 1;
			return 1;
		}
		break;
	case 3:
		//首先位置要有效,其次該位置沒有走過
		if (x - 2 >= 0 && y + 1 <= Y-1 && chess[x - 2][y + 1] == 0)
		{
			//如果有效位置,則將(x,y)改為馬下一次要到的位置
			x -= 2;
			y += 1;
			return 1;
		}
		break;
	case 4:
		if (x + 1 <= X - 1 && y + 2 <= Y - 1 && chess[x + 1][y + 2] == 0)
		{
			x += 1;
			y += 2;
			return 1;
		}
		break;
	case 5:
		if (x + 1 <= X - 1 && y - 2 >= 0 && chess[x + 1][y - 2] == 0)
		{
			x += 1;
			y -= 2;
			return 1;
		}
		break;
	case 6:
		if (x - 1 >= 0 && y + 2 <= Y - 1 && chess[x - 1][y + 2] == 0)
		{
			x -= 1;
			y += 2;
			return 1;
		}
		break;
	case 7:
		if (x - 1 >= 0 && y - 2 >= 0 && chess[x - 1][y - 2] == 0)
		{
			x -= 1;
			y -= 2;
			return 1;
		}
		break;
	default:
		break;
	}
	//返回0,需要重新尋找
	return 0;
}
//遍歷演算法
//x,y表示位置,tag表示當前走了幾格,第一次開始時該值應為1
int TravelChess(int x, int y, int tag)
{
	chess[x][y] = tag;
	//如果走了X*Y格,說明全部遍歷完,列印棋盤
	if (tag == X*Y)
	{
		print();
		return 1;//返回1表示遍歷完成
	}
	//如果沒有遍歷完
	int x1 = x, y1 = y;
	//尋找下一個有效位置
	int count = 0;
	int flag = nextxy(x1, y1, count);
	//不斷自增count,直到找到下一個有效位置
	while (flag == 0 && count <= 7)
	{
		count++;
		flag = nextxy(x1, y1, count);
	}
	//如果找到了下一個有效位置,該有效位置作為(x,y),繼續呼叫自身進行相同的搜尋
	while (flag)
	{
		//如果遍歷全部完成逐層返回1;
		if (TravelChess(x1, y1, tag + 1))
		{
			return 1;
		}
		//否則說明從該次的(x,y)往後走沒有走通,重新尋找該次呼叫的下一個有效位置
		x1 = x, y1 = y;
		count++;
		flag = nextxy(x1, y1, count);
		while (flag == 0 && count <= 7)
		{
			count++;
			flag = nextxy(x1, y1, count);
		}
	}
	//如果沒有找到有效位置,則將棋盤該位置重新賦值為0
	if (0 == flag)
	{
		chess[x][y] = 0;
	}
	//返回0代表這種走法不通,讓上一層呼叫重新尋找有效位置
	return 0;
	
}
int main()
{
	//記錄開始時間
	clock_t start = clock();
	//以防萬一,初始化棋盤都為0
	for (int i = 0; i < Y; i++)
	{
		for (int j = 0; j < X; j++)
		{
			chess[i][j] = 0;
		}
	}
	printf("請輸入馬的初始位置:");
	int x, y;
	scanf("%d,%d", &x, &y);
	if (!TravelChess(x, y, 1))
		printf("馬踏棋盤失敗了,請更換起始位置!\n");
	clock_t end = clock();
	printf("本次執行一共用時:%f", (double)(end - start) / CLOCKS_PER_SEC);

	return 0;
}

用深度優先搜尋寫的這個實在是太慢了,跑了很久不出結果,我也很絕望,於是就將棋盤換成了55的試了一下很快就出結果了!88的跑不出來,不知道自己的程式碼有沒有問題,用5*5的試了一下,感覺沒啥問題。 在這裡插入圖片描述