一.问题描述
中国象棋棋盘有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.棋盘可以任意修正

相关文章:

  • 2022-03-02
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-05-24
  • 2021-10-05
  • 2021-08-29
猜你喜欢
  • 2021-10-31
  • 2021-05-31
  • 2021-09-19
  • 2022-12-23
相关资源
相似解决方案