一.问题描述
中国象棋棋盘有10行9列,其中马的规则是“日”字形。现想让马从棋盘上任何一个位置开始,让马走遍所有的方格,并要求每个点仅走过一次。这就是马的周游(遍历)问题。这是一个NP问题。
具体算法如下:
#include <stdio.h>
int a[10][9],b[90],c[90];//用于在棋盘上打印周游顺序
struct Elem
{
char c;
int mark_direct[8];
};
Elem chessboard[14][13];/*10行9列,边界两个格用于检测*/
struct direct_increment
{
int dx;
int dy;
};
struct Postion
{
int x;
int y;
};
direct_increment direct_ay[8]={{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1},{-2,1},{-1,2}};
//初始化棋盘
void init_chessboard(Elem(*p)[13])
{
int i,j;
for(i=0;i<14;i++)
for(j=0;j<13;j++)
(p[i][j]).c='#';
for(i=2;i<=11;i++)
for(j=2;j<=10;j++)
{
(p[i][j]).c='0';
for(int k=0;k<8;k++)
((p[i][j]).mark_direct)[k]=0;
}
}
//检验点是否在棋盘上
bool check_postion(Postion pos)
{
if(pos.x<2||pos.x>11||pos.y<2||pos.y>10)
return false;
else return true;
}
//判断是不是最后一个没走的点
bool is_last_point(Elem(*p)[13],Postion pos)
{
char cc=p[pos.x][pos.y].c;
if(cc=='1')
return false;
else
for(int i=2;i<=11;i++)
for(int j=2;j<=10;j++)
if(p[i][j].c=='0'&&!(i==pos.x&&j==pos.y))
return false;
return true;
}
//判断结点是否被访问过
bool is_visited(Elem(*p)[13],Postion pos)
{
char cc=p[pos.x][pos.y].c;
if(cc=='0')
return false;
else
return true;
}
//标记结点为已走过
void mark_step(Elem(*p)[13],Postion pos)
{
p[pos.x][pos.y].c='1';
}
//判断是否完成遍历
bool visit_complete(Elem(*p)[13])
{
int count=0;
for(int i=2;i<=11;i++)
for(int j=2;j<=10;j++)
if(p[i][j].c=='1')
count++;
if(count==90)
return true;
else
return false;
}
//清除走过的点
void clear_pos(Elem(*p)[13],Postion pos)
{
p[pos.x][pos.y].c='0';
}
//统计当前结点的方向数
int count_way(Postion pos)
{
int counter=0;
for(int i=0;i<8;i++)
{
Postion pos1;
pos1.x=pos.x+direct_ay[i].dx;
pos1.y=pos.x+direct_ay[i].dy;
if((chessboard[pos.x][pos.y].mark_direct)[i]==0&&(chessboard[pos.x+direct_ay[i].dx][pos.y+direct_ay[i].dy]).c=='0')
++counter;
}
return counter;
}
//贪心法选择最佳下一落脚点
bool Select_bestpost(Postion *ppos,Postion *bpos)
{
int ct;
int i;
int direct,direct_ct=8;
Postion pos1,pos_best;
int flag=0;
for(i=0;i<8;i++)
{
if(((chessboard[ppos->x][ppos->y]).mark_direct)[i]==0)
{
pos1.x=ppos->x+direct_ay[i].dx;
pos1.y=ppos->y+direct_ay[i].dy;
if(check_postion(pos1)&& !is_visited(chessboard,pos1))
if(is_last_point(chessboard,pos1))
ct=1;
else
ct=count_way(pos1);
else
ct=-1;
if(ct<=direct_ct && ct>0)
{
pos_best.x=pos1.x;
pos_best.y=pos1.y;
flag=1;
direct_ct=ct;
direct=i;
}
}
else continue;
}
if(flag)
{
bpos->x=pos_best.x;
bpos->y=pos_best.y;
int reverse_direct=(direct)>=4?direct-4:direct+4;
(chessboard[ppos->x][ppos->y]).mark_direct[direct]=1;
(chessboard[bpos->x][bpos->y]).mark_direct[reverse_direct]=1;
return true;
}
else
return false;
}
//递归实现马的周游
bool Visit_Recursion(Elem(*p)[13],Postion pos)
{
static int u=90;
Postion next_pos;
static Postion pos_beg=pos;
int Succ=0;
static ct=0;
int i;
mark_step(chessboard,pos);
if(visit_complete(chessboard))
{
Succ=1;
ct=0;
return true;
}
else
for(i=0;!Succ&&i<8&&u>1;i++)
{
if(!Select_bestpost(&pos,&next_pos))
continue;
if(!Visit_Recursion(p,next_pos))
{
clear_pos(p,next_pos);
continue;
}
else
Succ=1;
++ct;
a[next_pos.x-2][next_pos.y-2]=u;
b[ct-1]=next_pos.x-1;
c[ct-1]=next_pos.y-1;
--u;
}
if(!Succ)
return false;
}
void main()
{
printf("****欢迎来到马的周游****\n\n");
int i,j,t=0,u,v;
Postion pos1;
init_chessboard(chessboard);
printf("初始化中国象棋棋盘为如下\n");
for(i=0;i<10;i++)
{
for(j=0;j<9;j++)
{
a[i][j]=0;
printf("%d ",a[i][j]);
}
printf("\n");
}
while(1)
{
printf("\n请输入马在棋盘中的起始位置(0<x<11,0<y<10),\n");
printf("用空格分隔,回车键确认\n\n");
scanf("%d%d",&u,&v);
pos1.x=u+1;
pos1.y=v+1;
printf("\n");
if(pos1.x<2||pos1.x>11||pos1.y<2||pos1.y>10)
{
printf("请输入正确的起始位置\n");
break;
}
else
{
if(Visit_Recursion(chessboard,pos1))
{
a[pos1.x-2][pos1.y-2]=1;
printf("遍历如下\n\n");
printf("%d:%d=>",pos1.x-1,pos1.y-1);
for(i=88;i>=0;i--)
{
if(i%10==0)
printf("\n");
if(i==0)
printf("%d:%d\n\n",b[i],c[i]);
else
printf("%d:%d=>",b[i],c[i]);
}
printf("马的顺序如下\n\n");
for(i=0;i<10;i++)
{
for(j=0;j<9;j++)
printf("%d ",a[i][j]);//在棋盘上打印遍历顺序
printf("\n");
}
printf("\n");
}
else
{
printf("没有满足条件的路径\n");
printf("\n");
printf("还要试一下其他位置吗?1:是,0:否\n");
scanf("%d",&t);
}
if(t==1)
continue;
else
break;
}
}
}
Ps:
1.棋盘可以任意修正