课程设计任务
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 周游路线问题