数据结构实验五-马踏棋盘

简介:
实验目的:
1、了解图的应用
2、加深对图结构及算法的理解
3、培养解决实际问题的编程能力
实验内容:
国际象棋马的走法:先直走或横走一格,再沿离开原来格子的方向斜走二格,合起来为一步棋;国际象棋棋盘黑白交错,格数8×8。
在这里插入图片描述
基本要求:
给出马从任意一个位置出发遍历整个棋盘的一条路径。
在1)的基础上给出从该位置出发的所有遍历路径

马踏棋盘

任务一
问题建模-将马踏棋盘问题用无向图建模,输出图的邻接表表示

任务描述
本关任务:将棋盘转换成图的邻接表表示模型,并输出邻接表。

相关知识
为了完成本关任务,你需要掌握:
1.问题建模
2.如何输出邻接表

- 问题建模

整个棋盘可表示为一个N×N的二维数组。假若马目前在位置(i,j)则马下一步可移动的位置最多有八个(如上图所示八个小黑点)。这八个位置(用0、1、……、7来标示)与当前位置在x,y方向上有固定位移差。0~7号位置(x,y)可以表示为:
(i-2,j+1),(i-1,j+2),(i+1,j+2),(i+2,j+1),
(i+2,j-1),(i+1,j-2),(i-1,j-2),(i-2,j-1)。
当然,这些位置一定在棋盘边界以内,即位置坐标(x,y)必须满足:
0<=x<=N-1 &&0<=y<=N-1

如何定义邻接关系?
马当前所在格子(i,j),马的下一步最多有八个格子可以到达。我们就认为当前格子和下一步能够到达的格子是“可达的”,这种“可达性”具有对称性。如果把格子看作一个顶点的话,那一个格子最多有8个格子可达,下一步马能到达的格子就是当前格子的邻接点。一个 N * N 棋盘可以转换成一个具有 N * N 个顶点的无向图。
为了方便,格子除了具有坐标属性(以格子左下角坐标表示),我们还可以给每个格子增加一个序号属性。
以5*5棋盘为例,假定我们按照以下方式对棋盘上的格子进行编号,那么编号与格子所在的坐标位置必然存在一定关系。

(0,4) , 20(1,4) , 21(2,4) , 22(3,4) , 23(4,4) , 24
(0,3) , 15(1,3) ,16(2,3) , 17(3,3) , 18(4,3) , 19
(0,2) ,10(1,2) , 11(2,2) , 12(3,2) , 13(4,2) , 14
(0,1) , 5(1,1) , 6(2,1) , 7(3,1) , 8(4,1) , 9
(0,0) , 0(1,0) , 1(2,0) , 2(3,0) , 3(4,0) , 4

对于每一个顶点(格子),它的邻接点都可以计算出来。
举例:以上图中6号格子为例
6号格子坐标(1,1)
根据偏移量,计算它的下一步的8个位置的坐标:
0~7号位置分别为:
(-1,2)(0,3)(2,3)(3,2)
(3,0)(2,-1) (0,-1) (-1,0)
其中:第1、5、6、7个位置越界,舍掉
因此6号格子(顶点)有4个邻接点。
既然我们能计算出每个顶点的邻接点,那我们就很容易生成图的邻接表了。
表头结点结构:
x y k link
边结点结构:
k next

其中
k:表头结点或边结点的序号
x:表头头结点所在的横坐标;
y:表头结点所在的纵坐标;
link:表头结点中指向边结点所在链表的表头结点。
next:边结点中指向后继的指针

- 邻接表的生成

在这里插入图片描述

- 编程要求

以N=8为例:根据提示,在右侧编辑器写出完整代码,实现邻接表的输出。

测试说明
测试输入:无
(在程序中#define N 8)
测试输出:
(0,0),0->17 10
(1,0),1->18 16 11
(2,0),2->19 17 8 12
(3,0),3->20 18 9 13
(4,0),4->21 19 10 14
(5,0),5->22 20 11 15
(6,0),6->23 21 12
(7,0),7->22 13
(0,1),8->25 2 18
(1,1),9->26 24 3 19
(2,1),10->27 25 16 0 4 20
(3,1),11->28 26 17 1 5 21
(4,1),12->29 27 18 2 6 22
(5,1),13->30 28 19 3 7 23
(6,1),14->31 29 20 4
(7,1),15->30 21 5
(0,2),16->33 1 10 26
(1,2),17->34 32 0 2 11 27
(2,2),18->35 33 24 8 1 3 12 28
(3,2),19->36 34 25 9 2 4 13 29
(4,2),20->37 35 26 10 3 5 14 30
(5,2),21->38 36 27 11 4 6 15 31
(6,2),22->39 37 28 12 5 7
(7,2),23->38 29 13 6
(0,3),24->41 9 18 34
(1,3),25->42 40 8 10 19 35
(2,3),26->43 41 32 16 9 11 20 36
(3,3),27->44 42 33 17 10 12 21 37
(4,3),28->45 43 34 18 11 13 22 38
(5,3),29->46 44 35 19 12 14 23 39
(6,3),30->47 45 36 20 13 15
(7,3),31->46 37 21 14
(0,4),32->49 17 26 42
(1,4),33->50 48 16 18 27 43
(2,4),34->51 49 40 24 17 19 28 44
(3,4),35->52 50 41 25 18 20 29 45
(4,4),36->53 51 42 26 19 21 30 46
(5,4),37->54 52 43 27 20 22 31 47
(6,4),38->55 53 44 28 21 23
(7,4),39->54 45 29 22
(0,5),40->57 25 34 50
(1,5),41->58 56 24 26 35 51
(2,5),42->59 57 48 32 25 27 36 52
(3,5),43->60 58 49 33 26 28 37 53
(4,5),44->61 59 50 34 27 29 38 54
(5,5),45->62 60 51 35 28 30 39 55
(6,5),46->63 61 52 36 29 31
(7,5),47->62 53 37 30
(0,6),48->33 42 58
(1,6),49->32 34 43 59
(2,6),50->56 40 33 35 44 60
(3,6),51->57 41 34 36 45 61
(4,6),52->58 42 35 37 46 62
(5,6),53->59 43 36 38 47 63
(6,6),54->60 44 37 39
(7,6),55->61 45 38
(0,7),56->41 50
(1,7),57->40 42 51
(2,7),58->48 41 43 52
(3,7),59->49 42 44 53
(4,7),60->50 43 45 54
(5,7),61->51 44 46 55
(6,7),62->52 45 47
(7,7),63->53 46

  • 示例代码如下:(温馨提示:本文全部代码只在 EduCoder 平台上通过测试,仅供参考,如有运行错误请自行改正)
#include<stdio.h>
#include<stdlib.h>
#define N 8
typedef struct node
{
	int k;
	struct node *next;
}edgenode;
typedef struct
{
	int x,y;
	int k;
	edgenode *link;
}vexnode;
vexnode ga[N*N];

int PX[8]={2,2,1,-1,-2,-2,-1,1};
int PY[8]={1,-1,-2,-2,-1,1,2,2};

void creatadjlist(vexnode ga[])  //创建邻接表
{
	int i,j,k;
	int xx,yy,newx,newy,newk;
	edgenode *s;
	edgenode *p;
	for(j=0;j<N;j++)
	for(i=0;i<N;i++)
	{
		k=N*j+i;
		ga[k].x=i;
		ga[k].y=j;
		ga[k].k=k;
		ga[k].link=NULL;
	}
	for(i=0;i<N*N;i++)
	{
		xx=ga[i].x;
		yy=ga[i].y;
		for(j=0;j<8;j++)
		{
			newx=xx+PX[j];
			newy=yy+PY[j];
			if(newx>=0 && newx<N  &&  newy>=0 && newy<N)   //头插法
			{
				newk=N*newy+newx;
				s=(edgenode *)malloc(sizeof(edgenode));
				s->k=newk;
				s->next=ga[i].link;
				ga[i].link=s;
			}
		}
	}


    for(i=0;i<N*N;i++)
	{
	printf("(%d,%d),%d->",ga[i].x,ga[i].y,ga[i].k);
	p=ga[i].link;
	while(p)
	{
		printf("%d ",p->k);
		p=p->next;
		
	}
	printf("\n");
    }

}


int main()
{
	creatadjlist(ga);
	return 0;
}

任务二
遍历算法的实现

任务描述
给出马从任意一个位置出发遍历整个棋盘的一条路径。

  • 示例代码如下:(温馨提示:本文全部代码只在 EduCoder 平台上通过测试,仅供参考,如有运行错误请自行改正)
#include<stdio.h>
#include<stdlib.h>
#define N 6
typedef struct node
{
	int k;
	struct node *next;
}edgenode;
typedef struct
{
	int x,y;
	int k;
	edgenode *link;
}vexnode;

typedef struct
{
    edgenode *data[MAXSIZE];
    int top;
}stack s;
stack s;

void push(stack *s,edgenode *x)
{if (s->top == MAXSIZE-1)
printf("overflow!");
s->top++;
s->data[s->top]=x;
}

edgenode* pop(stack *s)
{
    if(s->top==-1)
    {
        printf("overflow!"); return NULL;
    }
    return s->data[s->top--];
}
void output(stack *s)
{
    int i;
    for(i=0;i<=s->top;i++)
    printf("%d ",s->data[i]->k);
}
vexnode ga[N*N];
int visited[N*N]={0};
int path[N*N]={-1};
int PX[8]={2,2,1,-1,-2,-2,-1,1};
int PY[8]={1,-1,-2,-2,-1,1,2,2};

void creatadjlist(vexnode ga[])
{
    int i,j,k;
    int xx,yy,newx,newy,newk;
    edgenode *s;
    edgenode *p;
    for (j=0;j<N;j++)
    for (i=0;i<N;i++)
    {
        k=N*j+i;
        ga[k].x=i;
        ga[k].y=j;
        ga[k].k=k;
        ga[k].link=NULL;
    }
    for (i=0;i<N*N;i++)
    {
        xx=ga[i].x;
        yy=ga[i].y;
        for(j=0;j<8;j++)
        {
            newx=xx+PX[j];
            newy=yy+PY[j];
            if(newx>=0 && newx<N && newy>=0 && newy<N)
            {
                newk=N*newy+newx;
                s=(edgenode *)malloc(sizeof (edgenode));
                s->k=newk;
                s->next=ga[i].link;
                ga[i].link=s;
            }
        }
    }
}
for (i=0;i<N*N;i++)
{
    printf("(%d,%d),%d->",ga[i].x,ga[i].y,ga[i].k);
    p=ga[i].link;
    while(p)
    {
        printf("%d ",p->k);
        p=p->next;
    }
    printf("\n");
    }  
}
void path_list()
{
    int i;
    for (i=0;i<N*N;i++)
    printf("(%d,%d)" ,i,path[i]);
}
dfs(int i)
{
    edgenode *p;
    p=ga[i].link;
    visited[i]=1;
while(s.top!=-1||p)
{
    while(p)
    {
        if (!visited[p->k])
        {visited[p->k]=1; push(&s,p);
        p=ga[p->k].link;
        }
        else p=p->next;
    }
    if(s.top==N*N-2)
    {
        printf("succeed!");
        printf("起点是0,依次经过的顶点序列:\n");
        output(&s);return 1;
    }
    if(s.top!=-1)
    {
        p=pop(&s);visited[p->k]=0;
        p=p->next;
    }
}
return 0;
}

int main()
{
    s.top=-1;
    creatadjlist(ga);
    dfs(0);
    printf("---------------------");
    return 0;
}

我把我目前写的关于数据结构 题目 的链接全部汇总整理在下面,有需要的小伙伴自己点击哈。

实验:

最近在学习最优化、运筹学和计量经济学的相关知识,实验课马上就要开了,不知道大家有没有兴趣。如果后期有时间,学习结束后,我可以整理一下上实验课的实验报告和题目,一起学习交流~

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
问题描述:将随机放在国际象棋的 8X8 棋盘中的某个方格中,按走棋规则进行移动。要求每个方格上只进入一次,走遍棋盘上全部 64 个方格。编制递归程序,求出的行走路线 ,并按求出的行走路线,将数字 1,2,…,64 依次填入 8X8 的方阵输出之。测试数据:由读者指定可自行指定一个的初始位置。实现提示:每次在多个可走位置中选择一个进行试探,其余未曾试探过的可走位置必须用适当结构妥善管理,以备试探失败时的“回溯”悔棋使用。并探讨每次选择位置的“最佳策略”,以减少回溯的次数。 背景介绍: 国际象棋为许多令人着迷的娱乐提供了固定的框架,而这些框架常独立于游戏本身。其中的许多框架都基于骑士奇异的L型移动规则。一个经典的例子是骑士漫游问题。从十八世纪初开始,这个问题就引起了数学家和解密爱好者的注意。简单地说,这个问题要求从棋盘上任一个方格开始按规则移动骑士,使之成功的游历国际象棋棋盘的64个方格,且每个方格都接触且仅接触一次。 可以用一种简便的方法表示问题的一个解,即将数字1,……,64按骑士到达的顺序依次放入棋盘的方格中。 一种非常巧妙的解决骑士漫游地方法由J.C.Warnsdorff于1823年给出。他给出的规则是:骑士总是移向那些具有最少出口数且尚未到达的方格之一。其中出口数是指通向尚未到达方格的出口数量。在进一步的阅读之前,你可以尝试利用Warnsdorff规则手工构造出该问题的一个解。 实习任务: 编写一个程序来获得棋盘即骑士漫游问题的一个解。 您的程序需要达到下面的要求: 棋盘的规模是8*8; 对于任意给定的初始化位置进行试验,得到漫游问题的解; 对每次实验,按照棋盘矩阵的方式,打印每个格被行径的顺序编号。 技术提示: 解决这类问题的关键是考虑数据在计算机中的存储表示。可能最自然的表示方法就是把棋盘存储在一个8*8的二维数组board中。以(x,y)为起点时骑士可能进行的八种移动。一般来说,位于(x,y)的骑士可能移动到以下方格之一:(x-2,y+1)、(x-1,y+2)、(x+1,y+2)、(x+2,y+1)、(x+2,y-1)、(x+1,y-2)、(x-1,y-2)、(x-2,y-1)。但请注意,如果(x,y)的位置离某一条边较近,有些可能的移动就会把骑士移到棋盘之外,而这当然是不允许的。骑士的八种可能的移动可以用一个数组MoveOffset方便地表示出来: MoveOffset[0]=(-2,1) MoveOffset[1]=(-1,2) MoveOffset[2]=(1,2) MoveOffset[3]=(2,1) MoveOffset[4]=(2,-1) MoveOffset[5]=(1,-2) MoveOffset[6]=(-1,-2) MoveOffset[7]=(-2,-1) 于是,位于(x,y)的骑士可以移动到(x+MoveOffset[k].x, y+MoveOffset[k].y),其中k是0到7之间的某个整数值,并且新方格必须仍位于棋盘上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值