实验要求:(满分10分)
1. (2分)基于教材和课件讲解内容,利用自己实现的栈结构完成可运行的迷宫求解程序
2. (2分)实现教材或课件中未给出的“可通”函数、“足迹打印”函数、“下一位置”函数、“打印不能通过的位置”函数等功能函数
3. (2分)实现MazeType数据类型,及可能会用到的数据对象(如,入口、出口、位置属性是墙或通路)、数据关系(如,位置之间的相邻关系)、基本操作(如,返回某个坐标位置是墙还是通路)
4. (2分)测试有通路和没通路等不同结果的输入迷宫
5. (2分)尝试进一步完善迷宫求解程序,使得从入口到出口有多条简单路径的迷宫的所有路径都能求解出来,或者从多条可行的路径中给出最短路径。
拓展要求:(附加3分)
1.(2分) 通过实验结果对比“入口-出口相对方向”和“探索方向的优先顺序”一致或不一致时,迷宫求解程序的运行效率。例如,当出口在入口的右下方向时,探索优先顺序是右下左上,或者上左下右时,程序运行“时间/效率/试过的位置数”是不一样的
2.(1分)分析“可通”函数原理,解释为什么迷宫求解程序得到的路径不会有环
实验时长:
3周完成
C语言代码如下:
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#define MaxSize 100
#define M 8
#define N 8
int mg[M+2][N+2]={//+2是为了构造围墙
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1}, //2(0->1)即可不通
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
int i,j,di,i1,j1,t=3,count=0;
int length[2][4] = {MaxSize};
int number1 = 0,number2 = 0; //统计位置数
//迷宫栈基本运算
typedef struct{
int i; //当前方块的行号
int j; //当前方块的列号
int di; //di是下一可走相邻方位的方位号
} Box;
typedef struct{
Box data[MaxSize]; //存放方块
int top; //栈顶指针
} StType; //定义栈类型
//实验要求
typedef struct{
int xi,yi;//入口坐标
int xe,ye;//出口坐标
int MT[M+2][N+2];//位置属性是墙还是通路
void MTInit(); //默认初始化方案
void rela();//位置之间的相邻关系
void info();//返回某个坐标是墙或者通路
} MazeType;
void MazeType::MTInit(){//默认初始化方案
for(int i=0;i<M+2;i++){
for(int j=0;j<N+2;j++){
this->MT[i][j] = mg[i][j];
}
}
}
void MazeType::info(){ //返回某个坐标是墙或通路
int x,y;
printf("请输入坐标:");
scanf("%d %d",&x,&y);
if(x>=M+2 || y>=N+2){//越界
printf("坐标越界!\n");
}else if(this->MT[x][y]==0){//通路
printf("通路!\n");
}else{//墙
printf("墙!\n");
}
}
void MazeType::rela(){ //位置之间的相邻关系
int x1,y1,x2,y2;
printf("请依次输入两点坐标,以判断其相邻关系:");
scanf("%d %d",&x1,&y1);
scanf("%d %d",&x2,&y2);
int direct = 4;
if(x1>=M+2 || y1>=N+2 || x2>=M+2 || y2>=N+2){//越界
direct = -1;
}else if(x1==x2){//0123上右下左
if(y1==y2+1){
direct = 2;//以1为参照
}else if(y1==y2-1){
direct = 0;
}else{
direct = 4;//不相邻
}
}else if(y1==y2){
if(x1==x2+1){
direct = 3;//以1为参照
}else if(x1==x2-1){
direct = 1;
}else{
direct = 4;//不相邻
}
}
switch(direct){
case -1:printf("输入的坐标越界!\n",x2,y2,x1,y1);break;//越界
case 0:printf("(%d,%d)是(%d,%d)的上邻。\n",x2,y2,x1,y1);break;//往上
case 1:printf("(%d,%d)是(%d,%d)的右邻。\n",x2,y2,x1,y1);break;//往右
case 2:printf("(%d,%d)是(%d,%d)的下邻。\n",x2,y2,x1,y1);break;//往下
case 3:printf("(%d,%d)是(%d,%d)的左邻。\n",x2,y2,x1,y1);break;//往左
default:printf("两点不相邻!\n");//不相邻
}
}
//栈结构
void InitStack(StType *&s){ //初始化栈
s=(StType *)malloc(sizeof(StType));
s->top=-1;
}
void DestroyStack(StType *&s){ //销毁栈
free(s);
}
bool StackEmpty(StType *s){ //判断栈是否为空
return(s->top==-1);
}
bool Push(StType *&s,Box e){ //入栈元素e
if (s->top==MaxSize-1){//栈溢出则报错
return false;
}
s->top++;
s->data[s->top]=e;
return true;
}
bool Pop(StType *&s,Box &e){ //出栈元素e
if (s->top==-1){//栈为空则报错
return false;
}
e=s->data[s->top];
s->top--;
return true;
}
bool GetTop(StType *s,Box &e){ //取栈顶元素e
if (s->top==-1){//栈为空则报错
return false;
}
e=s->data[s->top];
return true;
}
void displaymg(int flag){
if(flag==0){
printf("迷宫MazeType信息如下:\n");
}else if(flag==1){
printf("路径按字母顺序展示:\n");
}
printf(" ");
for(int i=0;i<10;i++){
printf("%d",i);
}
printf("\n");
for(int i=0;i<10;i++){
printf("%d",i);
for(int j=0;j<10;j++){
if(mg[i][j]==1){//墙
printf("*");
}else if(mg[i][j]==0){
printf(" ");
}else{//路径
printf("%c",'a'+(mg[i][j]-2)%26);
}
}
printf("\n");
}
}
//实验要求
bool Pass(int flag){//“可通”函数
bool find=false;
while (di<4 && !find){ //找相邻可走方块(i1,j1)
di++;
switch(flag){//通过flag进行顺序选择
case 0:switch(di){//1230右下左上顺时针
case 1:i1=i-1; j1=j; break;//往上
case 2:i1=i; j1=j+1; break;//往右
case 3:i1=i+1; j1=j; break;//往下
case 0:i1=i; j1=j-1; break;//往左
};break;
case 1:switch(di){//1032上左下右逆时针
case 1:i1=i-1; j1=j; break;//往上
case 0:i1=i; j1=j+1; break;//往右
case 3:i1=i+1; j1=j; break;//往下
case 2:i1=i; j1=j-1; break;//往左
};break;
// case 0:switch(di){//0123上左下右顺时针
// case 0:i1=i-1; j1=j; break;//往上
// case 1:i1=i; j1=j+1; break;//往右
// case 2:i1=i+1; j1=j; break;//往下
// case 3:i1=i; j1=j-1; break;//往左
// };break;
// case 1:switch(di){//1230上右下左顺时针
// case 1:i1=i-1; j1=j; break;//往上
// case 2:i1=i; j1=j+1; break;//往右
// case 3:i1=i+1; j1=j; break;//往下
// case 0:i1=i; j1=j-1; break;//往左
// };break;
// case 2:switch(di){//2301上右下左顺时针
// case 2:i1=i-1; j1=j; break;//往上
// case 3:i1=i; j1=j+1; break;//往右
// case 0:i1=i+1; j1=j; break;//往下
// case 1:i1=i; j1=j-1; break;//往左
// };break;
// case 3:switch(di){//3012上右下左顺时针
// case 3:i1=i-1; j1=j; break;//往上
// case 0:i1=i; j1=j+1; break;//往右
// case 1:i1=i+1; j1=j; break;//往下
// case 2:i1=i; j1=j-1; break;//往左
// };break;
}
if (mg[i1][j1]==0){ //找到一个相邻可走方块,设置find为真
find=true;
}
}
return find;
}
int FootPrint(Box path[],int k){//“足迹打印”函数
int c=0;
while (k>=1){
k--;
c++;
printf("(%d,%d)",path[k].i,path[k].j);
if(k>=1){
printf("->");
}
if (c%5==0){ //每输出每5个方块后换一行
printf("\n");
}
}
printf("\n");
return c;//c是路径长度
}
void NextPos(StType *st,Box e){//“下一位置”函数
st->data[st->top].di=di; //修改原栈顶元素的di值
e.i=i1; e.j=j1; e.di=-1;
//printf("下一位置为(%d,%d)\n",i1,j1);
Push(st,e); //相邻可走方块e进栈
}
void MarkPrint(int i,int j){//“打印不能通过的位置”函数
//printf("(%d,%d)不能通过\n",i,j);
}
//迷宫求解
bool mgpath(int flag,int xi,int yi,int xe,int ye){ //求解路径为:(xi,yi)->(xe,ye)
Box path[MaxSize], e;
StType *st; //定义栈st
InitStack(st); //初始化栈顶指针
e.i=xi; e.j=yi; e.di=-1; //设置e为入口
Push(st,e); //方块e进栈
mg[xi][yi]=2; //入口的迷宫值置为2避免重复走到该方块
while (!StackEmpty(st)){ //栈不空时循环
GetTop(st,e); //取栈顶方块e
i=e.i; j=e.j; di=e.di;
if (i==xe && j==ye){ //找到了出口,输出该路径
//printf("--------------------\n第%d条迷宫路径如下:\n",count+1);
int k=0;
while (!StackEmpty(st)){
Pop(st,e); //出栈方块e
path[k++]=e; //将e添加到path数组中
}
length[0][count] = count+1; //第一行存储序号
length[1][count++] = FootPrint(path,k); //第二行存储长度
printf("长度为:%d\n",length[1][count-1]);
DestroyStack(st); //销毁栈
return true; //输出一条迷宫路径后返回true
}
if (Pass(flag)){ //Pass()找到了一个相邻可走方块(i1,j1)
//printf("(%d,%d)可通\n",i1,j1);
NextPos(st,e);
mg[i1][j1]=t++; //(i1,j1)的迷宫值置为t(小写字母)避免重复走到该方块
if(flag==0){
number1++;
}else{
number2++;
}
//displaymg(1);
}else{ //没有路径可走,则退栈
MarkPrint(e.i,e.j);
Pop(st,e); //将栈顶方块退栈
mg[e.i][e.j]=0; //让退栈方块的位置变为其他路径可走方块
t--;
//displaymg(1);
}
}
DestroyStack(st); //销毁栈
return false; //表示没有可走路径,返回false
}
//最短路径
void BestPath(){
//对length按长度进行冒泡排序
for(int i=0;i<3;i++){
for(int j=0;j<3-i;j++){
if(length[1][j]>length[1][j+1]){
int c1 = length[1][j];
length[1][j] = length[1][j+1];
length[1][j+1] = c1;
int c2 = length[0][j];
length[0][j] = length[0][j+1];
length[0][j+1] = c2;
}
}
}
printf("\n最短路径为:第%d条 长度为:%d",length[0][0],length[1][0]);
}
int main(){
int startTime1,startTime2,endTime1,endTime2;
MazeType mt;
mt.MTInit();//储存mg信息
displaymg(0);//展示迷宫
int xi,yi,xe,ye;
printf("请输入入口坐标(xi yi):");
scanf("%d %d",&xi,&yi);
printf("请输入出口坐标(xe ye):");
scanf("%d %d",&xe,&ye);
printf("满足出口在入口的右下方向。\n");
for(int flag=0;flag<2;flag++){//4;flag++){//顺时针探索
if(flag==0){
startTime1 = clock();
//printf("startTime1 = %.2f",startTime1);
printf("\n“右下左上”探索开始!\n");
}else{
startTime2 = clock();
//printf("startTime2 = %.2f",startTime2);
printf("\n“上左下右”探索开始!\n");
}
if(mgpath(flag,xi,yi,xe,ye)){
displaymg(1);//展示路径
}else{
printf("无可实现的路径!");
}
for(int i=0;i<M+2;i++){//reset
for(int j=0;j<N+2;j++){
mg[i][j] = mt.MT[i][j];
}
}
t=3;
if(flag==0){
endTime1 = clock();
//printf("endTime1 = %.2f",endTime1);
float n1 = (float)length[1][0]/(float)number1*100;
printf("“右下左上”探索完毕,耗时%d秒,效率为%.1f%%,试过的位置数为%d个\n",endTime1-startTime1,n1,number1);
}else{
endTime2 = clock();
//printf("endTime2 = %.2f",endTime2);
float n2 = (float)length[1][1]/(float)number2*100;
printf("“上左下右”探索完毕,耗时%d秒,效率为%.1f%%,试过的位置数为%d个\n",endTime2-startTime2,n2,number2);
}
for(int i=0;i<M+2;i++){//清除路径进入下一轮探索
for(int j=0;j<N+2;j++){
mg[i][j] = mt.MT[i][j];
}
}
}
printf("(效率=最终路径长度/试过的位置数)\n");
//选最短路径
//BestPath();
return 0;
}
记录一下曾经的努力,欢迎大家围观与讨论!