1. 程式人生 > >馬踏棋盤,貪心演算法

馬踏棋盤,貪心演算法

由於回溯法實在是太耗費時間,所以利用貪心演算法進行了優化,在閱讀了網上關於貪心演算法怎麼用之後又除錯了很久,終於在上一個回溯法的基礎上寫出了優化的演算法,但是由於本人水平實在有限,程式碼或多或少還是有些問題,優化的可能還不夠。希望自己能繼續進步吧。 所謂貪心演算法:貪心演算法(又稱貪婪演算法),是指在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的區域性最優解。它只考慮區域性的最優,從而讓總體也最優。對於馬踏棋盤來說,我們每一步的選擇總是選擇下一步的下一步可選擇數最少的那一步,簡單點說,就是比如我下一次有的可選擇方案有a,b,c三種,然後a,b,c三種方案的下一步分別又有5,3,2種選擇,那麼我們下一步就選擇c方案。 那麼我們只需要在原來的演算法上在選擇的時候進行一個判斷就行了,原來我們的演算法是,只有找到了一種方案就執行,不管他的下一步,就會造成下一步的下一步無路可走,然後又回溯到上一步重新選擇。我在除錯的過程中發現我們選擇的是下一步的下一步方案最少的方案,那麼就能保證下一步的下一步不會碰壁,就這樣一直遞迴下去不會產生回溯,但是到了倒數第二步,也即是第63步,他的下一步只有一種選擇,他的下一步的下一步沒有選擇了,結束了,所以就需要在中間加上判斷,如果到了倒數第二步,就應該找到可行方案直接走。 程式碼如下:

#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;
}
//尋找該節點的所有的有效位置,返回有效位置的個數
int numofpos(int x, int y)
{
	int count = 0;
	int num = 0;
	int x1 = x, y1 = y;
	int flag = nextxy(x1, y1, count);
	while (count <= 7)
	{
		if (flag==1)
			num++;
		count++;
		x1 = x, y1 = y;
		flag = nextxy(x1, y1, count);
	}
	return num;
}

//選擇下一個節點的下一選擇數最少的那個
//nextnum存放的是下一個節點的下一個選擇的個數,
//num是陣列大小
int Minchoose(int nextnum[], int num)
{
	int min = 0;
	int index = 0;
	//找到第一個下一個選擇不為零的位置
	for (int i = 0; i < num; i++)
	{
		if (nextnum[i]>0)
		{
			min = nextnum[i];
			index = i;
			break;
		}
	}
	for (int i = 1; i < num; i++)
	{
		//找到選擇數大於0且最小的下一個節點
		if (nextnum[i]>0 && nextnum[i] < min)
		{
			min = nextnum[i];
			index = i;
		}
			
	}
	
	if (nextnum[index]>0)
		return index;
	//如果不存在選擇數大於0且數目最小的位置,返回-1,通知它的下一步沒有路了,也就是到了最後一步。
	return -1;

}
//遍歷演算法
//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);
	//存放(x,y)所有有效位置的下一個有效位置的個數,有效位置的編號是0~7
	int num[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

	//每找到一個有效位置,就計算該有效位置的下一位有多少種選擇
	while (count <= 7)
	{
		if (flag == 1)
		{
			//計算對於有效位置count的下一位有多少種選擇
			num[count] = numofpos(x1, y1);
		}

		//繼續
		count++;
		x1 = x, y1 = y;
		flag = nextxy(x1, y1, count);
	}


	int index = Minchoose(num, 8);
	x1 = x, y1 = y;
	//說明到了最後一步,找到可行方案即可
	if (index == -1)
	{
		count = 0;
		flag = nextxy(x1, y1, count);
		while (flag == 0 && count <= 7)
		{
			count++;
			x1 = x;
			y1 = y;
			flag = nextxy(x1, y1, count);
		}
	}
	else
	{
		//將下一個位置選擇第index個
		flag = nextxy(x1, y1, index);
	}
	
	//如果找到了下一個有效位置,該有效位置作為(x,y),繼續呼叫自身進行相同的搜尋
	if (TravelChess(x1, y1, tag + 1))
	{
		return 1;
	}
	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("本次執行一共用時:%fs", (double)(end - start) / CLOCKS_PER_SEC);

	return 0;
}

效率提高了不止一點。 在這裡插入圖片描述