設計一個國際象棋的馬踏棋盤的演示程序
基本要求
將馬放到國際象棋的8*8棋盤board上的某個方格中,馬按走棋規則進行移動,要求每個方格只進入一次,走遍棋盤上的64個方格,編寫遞歸程序,求出馬的行走路線,並按求出路線,將數字12 3.。。。。64依次填入一個8*8的方陣,輸出之
測試數據
初始位置由程序使用者指定,(i, j) 注:(0<=i<8) (0<=j<8)
首先這是一個搜索問題,運用深度優先搜索進行求解。算法如下:
1、輸入初始位置座標x,y。
2、計算當前座標的八個方位的子結點,選出那此可行的子結點。
循環遍歷所有可行子結點,重複2。
如果所有節點都不可行,出棧,選取下一子節點,重複步驟2。
顯然步驟2是一個反覆的過程,這樣做是完全可行的,它輸入的是全部解,但是馬遍歷當8×8時解是非常之多的,用天文數字形容也不爲過,這樣一來求解的過程就非常慢,並且出一個解也非常慢。怎麼才能快速地得到部分解呢?
【貪心算法】
其實馬踏棋盤的問題很早就有人提出,且早在1823年,J.C.Warnsdorff就提出了一個有名的算法。在每個結點對其子結點進行選取時,優先選擇‘出口’最小的進行搜索,‘出口’的意思是在這些子結點中它們的可行子結點的個數,也就是‘孫子’結點越少的越優先跳,爲什麼要這樣選取,這是一種局部調整最優的做法,如果優先選擇出口多的子結點,那出口少的子結點就會越來越多,很可能出現‘死’結點(顧名思義就是沒有出口又沒有跳過的結點),這樣對下面的搜索純粹是徒勞,這樣會浪費很多無用的時間,反過來如果每次都優先選擇出口少的結點跳,那出口少的結點就會越來越少,這樣跳成功的機會就更大一些。這種算法稱爲爲貪心算法,也叫貪婪算法或啓發示算法,它對整個求解過程的局部做最優調整,它只適用於求較優解或者部分解,而不能求最優解。這樣的調整方法叫貪心策略,至於什麼問題需要什麼樣的貪心策略是不確定的,具體問題具體分析。實驗可以證明馬遍歷問題在運用到了上面的貪心策略之後求解速率有非常明顯的提高,如果只要求出一個解甚至不用回溯就可以完成,
在前面的算法基礎之上,增添一些程序加以實現:
以下附代碼
/*
* =====================================================================================
*
* Filename: horse.c
*
* Description:
*
* Version: 1.0
* Created: 2013年09月13日 22時39分47秒
* Revision: none
* Compiler: gcc
*
* Author: gaoyuan,
* Company: Class 1204 of Computer Science and Technology
*
* =====================================================================================
*/
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
#define STACKINCREASE 10
#define N 8
#define TRUE 1
#define FALSE 0
int i_i, j_j;
int tmp_p[N][N] = {0};
typedef struct chess
{
int x;
int y;
int dir_choose;
int flag;
// int dir[8];
}CHESS;
int map[N][N][8]; //存放下一步方向
int board[N][N][8];
int weight[N][N];
CHESS choose[8] = {{-2, 1, 0}, {-1, 2, 0}, {1, 2, 0}, {2, 1, 0}, {2, -1, 0}, {1, -2, 0}, {-1, -2, 0}, {-2, -1, 0}};
typedef struct SeqStack
{
CHESS *base;
CHESS *top;
int size; //已用空間
int len;
}SqStack;
void init_Stack(SqStack *s)
{
s->base = (CHESS *)malloc(MAXSIZE * sizeof(CHESS));
if (!s->base)
{
printf("失敗\n");
exit (0);
}
s->top = s->base;
s->size = MAXSIZE;
s->len = 0;
}
int if_empty(SqStack *s)
{
if (s->len == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
int top_stack(SqStack *s, CHESS *p)
{
if (s->top > s->base) //如果棧非空
{
*p = *(s->top - 1);
return TRUE;
}
else
{
return FALSE;
}
}
int change_stack(SqStack *s, CHESS *p, int x, int y, int z) //待定
{
int k;
if (s->top > s->base)
{
p->dir_choose = board[x][y][z + 1];
p->flag = z + 1;
*(s->top - 1) = *p;
return TRUE;
}
else
{
return FALSE;
}
}
int push_stack(SqStack *s, CHESS *p)
{
if (s->top - s->base == s->size)
{
s->base = (CHESS *)realloc(s->base, (s->size + STACKINCREASE) * sizeof(CHESS));
s->top = s->base + s->size;
s->size += STACKINCREASE;
}
*s->top = *p;
s->top++;
s->len++;
return TRUE;
}
int pop_stack(SqStack *s, CHESS *p)
{
if ((*s).base == (*s).top)
{
return FALSE;
}
*p = *(s->top - 1);
s->top--;
s->len--;
}
void setweight()
{
int i, j, k;
int x, y;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
weight[i][j] = 0;
for (k = 0; k < 8; k++)
{
x = choose[k].x + i;
y = choose[k].y + j;
if (x >= 0 && x < N && y >= 0 && y < N)
{
weight[i][j]++; //當前位置有多少方向可以移動
}
}
}
}
}
int check(int x, int y)
{
if (x < 0 || x >= N || y < 0 || y >= N)
{
return 0;
}
return 1;
}
void setmap()
{
int a[8];
int i, j, k, m, min, s, h;
int b;
CHESS n1, n2;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
for (h = 0; h < 8; h++)
{
n1.x = i;
n1.y = j;
n2.x = i + choose[h].x;
n2.y = j + choose[h].y;
if (check(n2.x, n2.y) != 0)
{
map[i][j][h] = weight[n2.x][n2.y];
board[i][j][h] = h;
}
else
{
map[i][j][h] = 9;
board[i][j][h] = 9;
}
}
for (m = 0; m < 7;m++)
{
for (k = m + 1; k < 8; k++)
{
if (map[i][j][m] > map[i][j][k])
{
min = map[i][j][m];
map[i][j][m] = map[i][j][k];
map[i][j][k] = min;
min = board[i][j][m];
board[i][j][m] = board[i][j][k];
board[i][j][k] = min;
}
}
// printf("%d\t", map[i][j][m]);
}
printf("\n");
}
}
getchar();
}
void show(int a[][N], int x, int y)
{
int i, j;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
printf("%5d", a[i][j]);
}
printf("\n");
}
}
void path(int x, int y)
{
int tmp_x, tmp_y, tmp_flag, flag;
int a[N * N] = {0}, b[N * N] = {0};
SqStack s;
int num = 0;
int i = 0;
init_Stack(&s);
CHESS local;
CHESS next_step;
local.x = x;
local.y = y;
local.dir_choose = board[x][y][0];
local.flag = 0;
tmp_p[x][y] = num + 1;
push_stack(&s, &local);
while (1)
{
if (if_empty(&s) == TRUE)
{
break;
}
top_stack(&s, &local);
tmp_x = local.x;
tmp_y = local.y;
tmp_p[tmp_x][tmp_y] = num+1;
tmp_flag = local.dir_choose;
flag = local.flag;
if (num == (N * N - 1))
{
printf("%d end\n", if_empty(&s));
break;
}
show(tmp_p, N, N);
sleep(0.5);
system("clear");
// getchar();
if (flag >= weight[tmp_x][tmp_y]) //所有方向已經走完
{
tmp_p[tmp_x][tmp_y] = 0;
pop_stack(&s, &local);
top_stack(&s, &local);
flag = local.flag ;
num--;
change_stack(&s, &local, local.x, local.y, flag);
}
else if (flag < weight[tmp_x][tmp_y])
{
next_step.x = tmp_x + choose[tmp_flag].x;
next_step.y = tmp_y + choose[tmp_flag].y;
if (check(next_step.x, next_step.y) && tmp_p[next_step.x][next_step.y] == 0)
{
num++;
flag = 0;
next_step.dir_choose = board[next_step.x][next_step.y][flag];
next_step.flag = 0;
push_stack(&s, &next_step);
}
else
{
change_stack(&s, &local, tmp_x, tmp_y, flag);
flag++;
}
}
}
show(tmp_p, N, N);
}
int main(int argc, char *argv[])
{
int x, y;
int i, j, m;
SqStack p;
init_Stack(&p);
for (i = 0; i < N; i++)
{
for(j = 0; j < N; j++)
{
weight[i][j] = 0;
for (m = 0;m < N; m++)
board[i][j][m] = 0;
}
}
printf("請輸入馬起始的x座標:");
scanf("%d", &x);
printf("請輸入馬起始的y座標:");
scanf("%d", &y);
if (check(x, y) < 0)
{
printf("輸入錯誤,請重新鍵入\n");
return 0;
}
setweight();
for (i = 0;i < N; i++)
{
for (j = 0;j < N; j++)
{
printf("%5d", weight[i][j]);
}
printf("\n");
}
// getchar();
// getchar();
setmap();
path(x, y);
return EXIT_SUCCESS;
}