马的Hamilton 周游路线问题 算法设计与分析

课程设计任务

1.课程设计的目的

 这门课程是一门实践性非常强的课程,要求我们能够将所学的内容应用到实际中,灵活解决实际问题。通过这次课程设计,能够培养我们独立思考、综合分析与动手的能力,并能加深对课堂所学理论和概念的理解,可以训练我们程序设计的思维和培养解决实际问题的分析能力。

2.课程设计的基本要求:

在处理每一个题目的时候,要从分析题目的需求入手,按设计(抽象)数据类型、构思算法、编制程序代码并调试的步骤完成题目,最终写出完整的分析报告。具体如下:
(1)选题要恰当,适合自身即可,不要为了最终的分数而追求高难、复杂的题目。
(2)设计思路清晰,复杂度较低。
(3)对编写的程序进行调试、修改和算法优化;
(4)课程设计报告和总结撰写规范,段落清晰,内容详实。
(5)严格遵守课程设计纪律,不迟到早退。
3.课程设计的创新要求:
(1)在基本要求达到后,可进行创新设计,如:定义结构体、类实现,调用C/C++标准库文件等;
(2)完成的课程设计内容超过指导教师演示难度,综合性较强。

课程设计报告

一、题目分析

1.1问题描述

的国际象棋棋盘上的一只马,恰好走过除起点外的其他个位置各一次,最后回到起点。这条路线称为马的Hamilton周游路线。对于给定的的国际象棋棋盘,和均为大于的偶数,且,试设计一个分治算法找出马的一条Hamilton周游路线。

1.2问题分析

在n*n的国际象棋棋盘上的一只马,可按8个不同方向移动。当n为奇数时,该问题无解。事实上,由于马在棋盘上移动的方格是黑白相间的,如果有解,则走到的黑白格子数相同,因此棋盘格子总数应为偶数,然而n为奇数,此为矛盾。下面给出的算法可以证明,当n≥6是偶数时,问题有解,而且可以用分治法在线性时间内构造出一个解。

二、功能的设计与思路

构造结构化的解:
1.|m-n|<=2,m,n>5,所以说最小的解是6-6,按照|m-n|<2规则可以继续扩展mm,m*(m+2)………在哪停止?当可以构造出6的2倍数也就是12的时候,结构化的解构造完毕。
2.证明可以由基础解求得原问题的解因为问题的可行域在|m-n|<=2,m,n>5范围内,二我们已经构造了6-12的结构化解,那么所有的解都能用6-12的解构造出,这是分治法核心

三、算法设计与分析

划分和合并
1.划分:将棋盘尽可能平均地分割成4块。当m,n=4k时,分割为2个2k;当m,n=4k+2时,分割为1个2k和1个2k+2划分点记作nn1和mm1两个原因,子问题必须是偶数,所以取模4,原问题是偶数所以只有4k和4k+2两种规模
2. 合并:合并方法采用临近合并,合并如图所示的8个点

四、代码实现

#include<cstdio>
#include<string.h>
int direct[8][2]={{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1}};					//马的八个移动方向
int ChessBoard[500][500][2];
const int B6_6[6][6][2]={																	//基本结构化棋盘,数字代表移动方向在direct中的下标,每格有两个移动方向
	{{0,7},{7,6},{0,5},{5,0},{7,5},{6,5}},
	{{1,0},{6,1},{1,4},{1,7},{6,4},{4,5}},
	{{2,7},{3,0},{4,3},{1,0},{7,2},{6,3}},
	{{7,2},{6,7},{5,0},{4,2},{3,7},{4,6}},
	{{0,1},{3,0},{0,5},{0,5},{4,2},{5,3}},
	{{1,2},{1,3},{3,4},{4,1},{2,4},{3,4}}
},

B6_8[6][8][2]={
	{{0,7},{0,6},{5,0},{5,0},{0,6},{6,0},{5,7},{5,6}},
	{{0,1},{6,1},{6,4},{0,4},{4,1},{4,1},{7,4},{4,5}},
	{{2,7},{3,0},{7,4},{2,7},{7,2},{1,4},{2,7},{3,6}},
	{{7,2},{6,2},{5,6},{4,6},{6,5},{6,7},{5,7},{6,3}},
	{{0,1},{3,0},{1,5},{0,3},{3,1},{0,3},{2,5},{3,5}},
	{{1,2},{2,3},{2,4},{4,2},{1,2},{1,4},{3,2},{3,4}}
},

B8_8[8][8][2]={
	{{0,7},{7,6},{5,7},{0,5},{7,5},{5,0},{7,5},{6,5}},
	{{7,1},{1,6},{1,4},{6,1},{1,6},{1,4},{7,6},{4,5}},
	{{2,7},{3,6},{5,3},{3,7},{7,0},{1,3},{7,2},{6,3}},
	{{2,1},{7,3},{7,2},{2,0},{5,6},{2,0},{4,7},{6,3}},
	{{2,7},{3,6},{5,1},{0,7},{3,6},{4,3},{7,2},{4,3}},
	{{7,1},{7,6},{0,3},{2,3},{0,7},{7,4},{7,2},{3,6}},
	{{2,0},{3,0},{5,0},{2,5},{3,4},{5,0},{5,4},{5,3}},
	{{2,1},{1,3},{4,3},{4,1},{4,1},{3,1},{2,3},{4,3}}
},

B8_10[8][10][2]={
	{{0,7},{0,6},{5,0},{0,5},{0,5},{6,5},{5,0},{0,5},{7,5},{6,5}},
	{{0,1},{1,6},{1,4},{1,4},{4,1},{1,4},{1,4},{1,0},{4,7},{6,4}},
	{{2,0},{3,6},{6,4},{6,5},{0,2},{0,6},{7,5},{6,7},{7,2},{4,3}},
	{{2,7},{1,7},{4,7},{6,0},{1,7},{7,5},{0,4},{5,4},{7,2},{3,5}},
	{{2,7},{6,2},{5,2},{1,7},{2,0},{4,1},{5,2},{1,3},{3,4},{6,3}},
	{{7,1},{3,6},{3,2},{3,6},{7,1},{3,5},{4,3},{0,7},{5,7},{6,3}},
	{{0,2},{3,0},{5,0},{1,5},{3,0},{5,0},{5,1},{0,5},{5,2},{5,4}},
	{{2,1},{1,3},{2,4},{4,1},{4,1},{1,3},{4,1},{4,1},{3,2},{3,4}}
},

B10_10[10][10][2]={
	{{0,7},{0,6},{7,5},{6,0},{0,5},{6,5},{7,5},{5,0},{7,5},{6,5}},
	{{1,0},{6,0},{1,4},{1,4},{1,7},{4,1},{1,4},{1,0},{7,6},{4,6}},
	{{2,0},{3,6},{4,2},{4,3},{0,2},{5,6},{7,6},{6,3},{7,2},{4,3}},
	{{0,2},{7,6},{4,6},{7,1},{7,0},{3,0},{0,4},{2,6},{2,7},{6,3}},
	{{2,7},{0,7},{5,4},{7,0},{2,6},{2,7},{4,2},{4,3},{6,4},{6,3}},
	{{2,1},{2,6},{5,3},{7,4},{5,3},{4,3},{2,6},{6,5},{6,2},{3,5}},
	{{7,1},{3,6},{3,1},{2,5},{7,3},{1,5},{3,5},{1,2},{7,2},{6,5}},
	{{2,7},{1,6},{6,7},{1,5},{1,3},{2,5},{6,2},{1,2},{7,6},{6,5}},
	{{2,0},{1,3},{5,0},{1,0},{5,0},{5,3},{5,0},{1,0},{5,2},{5,3}},
	{{2,1},{3,2},{4,1},{3,1},{4,1},{4,2},{4,1},{2,1},{4,2},{4,3}}
},

B10_12[10][12][2]={
	{{0,7},{0,6},{6,5},{0,5},{7,6},{0,5},{7,5},{0,5},{0,6},{0,5},{7,6},{6,5}},
	{{1,7},{1,6},{5,4},{1,4},{1,6},{1,4},{6,5},{1,4},{7,6},{1,4},{7,4},{6,4}},
	{{2,1},{3,2},{5,6},{2,6},{1,0},{5,3},{6,7},{2,3},{5,7},{2,0},{7,2},{6,3}},
	{{2,1},{3,6},{7,0},{2,1},{5,0},{5,2},{4,1},{2,5},{5,7},{6,3},{7,2},{4,3}},
	{{0,7},{2,6},{2,1},{7,1},{4,0},{1,2},{4,1},{3,5},{0,6},{3,5},{7,2},{6,3}},
	{{2,0},{0,7},{5,4},{6,3},{0,6},{1,6},{4,5},{1,6},{7,2},{3,6},{5,4},{6,3}},
	{{2,1},{3,6},{4,5},{7,4},{1,3},{0,6},{5,4},{2,0},{5,1},{0,7},{7,2},{6,3}},
	{{1,7},{7,6},{3,2},{2,5},{2,1},{6,7},{2,1},{7,4},{2,5},{4,3},{7,2},{6,4}},
	{{2,0},{1,0},{5,0},{5,0},{2,3},{5,0},{1,0},{5,0},{5,0},{5,0},{3,2},{5,3}},
	{{2,1},{3,1},{4,3},{4,1},{4,2},{4,1},{3,1},{4,1},{4,3},{4,1},{4,2},{4,3}}
};

void DivideConquer(int x1,int y1,int x2,int y2);
void FillBoard(int *board,int x1,int y1,int x2,int y2,bool flag);
void CombineBoard(int x1,int y1,int x2,int y2);

void DivideConquer(int x1,int y1,int x2,int y2)
{
	//分治法主要函数
	int h=x2-x1+1,w=y2-y1+1,dw=w/4*2+w%2,dh=h/4*2+h%2;		//dw,dh分别是棋盘宽和高的一半,且保证了至少有一个是偶数,使得分治时不会出现无解的棋盘
	bool flag=false;
	if (w<h)
	{
		flag=true;
		int t=w;w=h;h=t;		//将棋盘翻转并标记,使得基本棋盘的内容能正确填入
	}
	if (h==6&&w==6) FillBoard((int*)B6_6,x1,y1,x2,y2,flag);		//填充基本棋盘
	if (h==6&&w==8) FillBoard((int*)B6_8,x1,y1,x2,y2,flag);
	if (h==8&&w==8) FillBoard((int*)B8_8,x1,y1,x2,y2,flag);
	if (h==8&&w==10) FillBoard((int*)B8_10,x1,y1,x2,y2,flag);
	if (h==10&&w==10) FillBoard((int*)B10_10,x1,y1,x2,y2,flag);
	if (h==10&&w==12) FillBoard((int*)B10_12,x1,y1,x2,y2,flag);

}
void FillBoard(int *board,int x1,int y1,int x2,int y2,bool flag)
{
	//实现基本棋盘的填充。flag为棋盘翻转标志
	int h=x2-x1+1,w=y2-y1+1;
	if (flag)
		for (int i=x1;i<=x2;i++)
			for (int j=y1;j<=y2;j++)
			{
				ChessBoard[i][j][0]=7-board[(i-x1+(j-y1)*h)*2];			//7-board,是利用了direct中互为转置的方向下标之和为7的细节
				ChessBoard[i][j][1]=7-board[(i-x1+(j-y1)*h)*2+1];
			}
	else
		for (int i=x1;i<=x2;i++)
			for (int j=y1;j<=y2;j++)
			{
				ChessBoard[i][j][0]=board[((i-x1)*w+j-y1)*2];
				ChessBoard[i][j][1]=board[((i-x1)*w+j-y1)*2+1];
			}
}
void CombineBoard(int x1,int y1,int x2,int y2)
{
	//合并棋盘,时间复杂度为O(1)
	int dw=(y2-y1+1)/4*2+(y2-y1+1)%2,dh=(x2-x1+1)/4*2+(x2-x1+1)%2;
	if (ChessBoard[x1+dh][y1+dw-1][0]==6) ChessBoard[x1+dh][y1+dw-1][0]=4;
	else ChessBoard[x1+dh][y1+dw-1][1]=4;
	if (ChessBoard[x1+dh+2][y1+dw-2][0]==2) ChessBoard[x1+dh+2][y1+dw-2][0]=1;
	else ChessBoard[x1+dh+2][y1+dw-2][1]=1;
	if (ChessBoard[x1+dh+1][y1+dw][0]==1) ChessBoard[x1+dh+1][y1+dw][0]=5;
	else ChessBoard[x1+dh+1][y1+dw][1]=5;
	if (ChessBoard[x1+dh][y1+dw+2][0]==5) ChessBoard[x1+dh][y1+dw+2][0]=4;
	else ChessBoard[x1+dh][y1+dw+2][1]=4;
	if (ChessBoard[x1+dh-1][y1+dw][0]==2) ChessBoard[x1+dh-1][y1+dw][0]=0;
	else ChessBoard[x1+dh-1][y1+dw][1]=0;
	if (ChessBoard[x1+dh-3][y1+dw+1][0]==6) ChessBoard[x1+dh-3][y1+dw+1][0]=5;
	else ChessBoard[x1+dh-3][y1+dw+1][1]=5;
	if (ChessBoard[x1+dh-2][y1+dw-1][0]==5) ChessBoard[x1+dh-2][y1+dw-1][0]=1;
	else ChessBoard[x1+dh-2][y1+dw-1][1]=1;
	if (ChessBoard[x1+dh-1][y1+dw-3][0]==1) ChessBoard[x1+dh-1][y1+dw-3][0]=0;
	else ChessBoard[x1+dh-1][y1+dw-3][1]=0;
}
int main()
{
	void DivideConquer(int x1,int y1,int x2,int y2);
    void FillBoard(int *board,int x1,int y1,int x2,int y2,bool flag);
    void CombineBoard(int x1,int y1,int x2,int y2);
	int m,n;
	printf("请输入棋盘大小m,n(|m-n|<=2):\n");
	scanf("%d%d",&m,&n);
	if (m*n%2||(m<6||n<6)) printf("该棋盘不存在骑士巡游回路\n");
	else if (n-m>2||m-n>2) printf("棋盘大小有误\n");
	else
	{
		int circuit[500][500],step=1,i,j,x,y;												//circuit记录每格的步序数
		printf("请输入马的起点坐标:");
		scanf("%d%d",&x,&y);
		if (x<=0||x>=m||y<=0||y>=n)	printf("非法的坐标\n");
		else
		{
			DivideConquer(0,0,m-1,n-1);
			memset(circuit,0,sizeof(circuit));
			i=--x;
			j=--y;
			do
			{
				circuit[i][j]=step++;															//模拟马走棋盘的过程
				int a=i,b=j;
				i+=direct[ChessBoard[a][b][0]][0];
				j+=direct[ChessBoard[a][b][0]][1];
				if (circuit[i][j]>1){
					i=a+direct[ChessBoard[a][b][1]][0];
					j=b+direct[ChessBoard[a][b][1]][1];
				}
			}while(i!=x||j!=y);
			printf("骑士巡游路线如下\n");
			for (int p=0;p<m;p++)
			{
				for (int q=0;q<n;q++)
					printf("%d\t",circuit[p][q]);
				printf("\n");
			}
		}
	}
	return 0;
}

五、运行结果与分析

在这里插入图片描述

参考文献
《算法习题解答》
《软件专题训练》
马的Hamilton 周游路线问题

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灏~川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值