迷宫问题

一、实验目的
1、问题描述
迷宫实验是取自心理学的一个古典实验。在该实验中,把一只老鼠从一个无顶大盒子的门放入,在盒中设置了许多墙,对行进方向形成了多处阻挡。盒子仅有一个出口,在出口处放置一块奶酪,吸引老鼠在迷宫中寻找道路以到达出口。对同一只老鼠重复进行上述实验,一直到老鼠从入口到出口,而不走错一步。老鼠经多次试验终于得到它学习走迷宫的路线。
2、设计功能要求
迷宫由m行n列的二维数组设置,0表示无障碍,1表示有障碍。设入口为(1,1),出口为(m,n),每次只能从一个无障碍单元移到周围四个方向上任一无障碍单元。编程实现对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。 
算法输入:代表迷宫入口的坐标
算法输出:穿过迷宫的结果。 算法要点:创建迷宫,试探法查找路。
二、算法难点
用栈存储迷宫,模拟走迷宫过程并将其可视化。
三、解决思路及算法
在迷宫中求出从入口到出口的路径。经分析,一个简单的求解方法是:从入口出发,沿某一方向进行探索,若能走通,则继续向前走;否则沿原路返回,换一方向再进行搜索,直到所有可能的通路都探索到为止。即所谓的回溯法。为了保证在任何位置上都能沿原路退回,显然需要用一个后进先出的结构来保存从入口到当前位置的路径,就是栈。
假设“当前位置”指的是“在搜索过程中某一时刻所在图中某个方块位置”,则求迷宫中一条路径的算法的基本思想是:若当前位置"可通",则纳入"当前路径",并继续朝“下一位置”探索,即切换“下一位置”为“当前位置”,如此重复直至到达出口;若当前位置“不可通”,则应顺着“来向”退回到“前一通道块”,然后朝着除“来向”之外的其他方向继续探索;若该通道块的四周四个方块均“不可通”,则应从“当前路径”上删除该通道块。所谓“下一位置”指的是“当前位置”四周四个方向(东、南、西、北)上相邻的方块。
我们首先定义栈,可用0表示通路,用1表示障碍,这样迷宫就可以用0、1矩阵来描述,然后用二维数组存储迷宫,m1代表墙,flag是标志域
迷宫中存在通路和障碍,为了方便迷宫的创建,可用0表示通路,用1表示障碍,这样迷宫就可以用0、1矩阵来描述, 
savefile函数是输入迷宫并将其存储于一个文件,以便日后调用该迷宫。
inread函数是从文件中读入迷宫,并给将文件中的迷宫加上围墙以便于后面的走迷宫。
zumigong函数:首先输入迷宫入口和出口。从迷宫的入口开始,如果该位置就是迷宫出口,则已经找到了一条路径,搜索工作结束。否则搜索其上、下、左、右位置是否是障碍,若不是障碍,就移动到该位置,然后再从该位置开始搜索通往出口的路径;若是障碍就选择另一个相邻的位置,并从它开始搜索路
利用递归的方式进行求解。从入口出发,按某一方向向前探索,若能走通(未走过的),即某处可以到达,则到达新点,否则试探下一方向;若所有的方向均没有通路,则沿原路返回前一点,换下一个方向再继续试探,直到所有可能的通路都探索到,或找到一条通路,或无路可走又返回到入口点,。在此过程中,将每一个经过的位置放入栈中,同时要根据判别方向改变行走的方向。
print1函数将以坐标形式输出迷宫路径并保存到文件中,在此过程中逐次pop出栈顶元素并将横纵坐标分别记录到相应数组中,最后逐次输出。

print2函数是以形象的矩阵形式输出迷宫路径,算法同print1函数。


四、测试样例





算法时间复杂度与空间复杂度的分析:
    时间复杂度:由于每个点都可能被放入探索路径中探索,因为会将探索过的点置为-1,所以每个点最多一次:O(mn)
空间复杂度:由于该算法中需要用到栈,所以空间复杂度为:O(mn)
五、反思启发
迷宫问题的难点在于记录路径,在走不通的情况下需要返回,若单单这么做则程序会反复试探一条路进入死循环,所以关键在于对于已经试探过的点我们要予以标记,以确保下次不重复探索该条路径,需要用两个栈每一个记录探索的路径,一个记录行走的路径。最后要注意在探索完路径后需要恢复迷宫的初始状态。
六、代码附录
#include<stdio.h>
#include<stdlib.h>
typedef struct
{
int mg[200];
int top;
}seqstack;
typedef struct migong
{
int m1;//存放墙
int flag;//标志域
}smaze[20][20];
void inista(seqstack &s)
{
s.top=-1;
}
int empty(seqstack &s)//判栈空
{
if(s.top==-1)
return 0;
else return 1;
}
void pop(seqstack &s,int &x1)//出栈
{
if(s.top==-1)
return ;
x1=s.mg[s.top];
s.top--;
}
void push(seqstack &s,int y1,int x1)//入栈
{
if(s.top==20)
return ;
s.top++;
s.mg[s.top]=y1;
s.top++;
s.mg[s.top]=x1;
}
void inread(smaze &maze,int m,int n)
{
    char name[20];
    int i,j;
    printf("输入存放迷宫的文件名\n");
    scanf("%s",name);
    FILE *fp=fopen(name,"rt");
    if(fp==NULL)
    {
        printf("\n打开文件%s失败\n");
        exit(1);
    }
    for(i=1;i<m+1;i++)
    for(j=1;j<n+1;j++)
    {
        fscanf(fp,"%d ",&maze[i][j].m1);
        if(maze[i][j].m1==0)
        maze[i][j].flag=0;
        else
        maze[i][j].flag=1;
    }
    fclose(fp);
    for(i=0;i<=m+1;i++)
    {
        maze[i][0].m1=1;
        maze[i][0].flag=1;//加两堵列墙
        maze[i][n+1].m1=1;
        maze[i][n+1].flag=1;
    }
    for(j=0;j<=n+1;j++)    //加两堵行墙
    {
        maze[0][j].m1=1;
        maze[m+1][j].m1=1;
        maze[0][j].flag=1;
        maze[m+1][j].flag=1;
    }
    for(i=0;i<=m+1;i++)
    {
        for(j=0;j<=n+1;j++)
        {
            if(maze[i][j].m1==1)
            printf("■");
            else
            printf("□");
        }
    printf("\n");
    }
}
void zumigong(seqstack &stack,smaze &maze,int &m,int &n)//走迷宫
{
int i,j,cout;
int x1,a,b,y1,x,y,x2,y2;
printf("输入入口的横纵坐标,空格隔开\n");
scanf("%d %d",&x,&y);
printf("输入出口的横纵坐标,空格隔开\n");
scanf("%d %d",&x2,&y2);
for(i=0;i<=m+1;i++)
{
for(j=0;j<=n+1;j++)
{
if(maze[i][j].m1==1)
printf("■");
else
printf("□");
}
printf("\n");
}
x1=x;
y1=y;
if(maze[x1][y1].m1==1)
{
printf("此路不通:\n");
return  ;
}
if(maze[x2][y2].m1==1)
printf("入口为墙。请重新输入\n");
else
while(x1!=x2||y1!=y2)
{
if(maze[x1][y1+1].m1==0&&maze[x1][y1+1].flag==0)
cout=0;
else if(maze[x1+1][y1].m1==0&&maze[x1+1][y1].flag==0)
cout=1;
else if(maze[x1-1][y1].m1==0&&maze[x1-1][y1].flag==0)
cout=2;
else if(maze[x1][y1-1].m1==0&&maze[x1][y1-1].flag==0)
cout=3;
else cout=4;
switch(cout)
{
case 0:
a=x1;
b=y1;
maze[x1][y1].flag=1;
y1=y1+1;
push(stack,b,a);//列先入
break;
case 1:
a=x1;
b=y1;
maze[x1][y1].flag=1;
x1=x1+1;
push(stack,b,a);
break;
case 2:
a=x1;
b=y1;
maze[x1][y1].flag=1;
x1=x1-1;
push(stack,b,a);
break;
case 3:
a=x1;
b=y1;
maze[x1][y1].flag=1;
y1=y1-1;
push(stack,b,a);
break;
case 4:
maze[x1][y1].flag=1;
if(empty(stack)!=0)
{
pop(stack,x1);
pop(stack,y1);
break;
}
else
{
printf("此路没有通路:\n");
return ;
}
}
}
push(stack,y2,x2);
}
void print1(seqstack stack)
{
    char name[20];
    int s1[20],s2[20],i=0,j=0,n=0;
    while(stack.top!=-1)
    {
        n++;
        pop(stack,s1[i]);
        pop(stack,s2[i]);
        i++;    j++;
    }
printf("以坐标形式输出迷宫路径\n");
printf("迷宫路径为:\n");
for(i=n-1;i>=1;i--)
printf("<%d,%d> → ",s1[i],s2[i]);
printf("<%d %d>\n",s1[0],s2[0]);
printf("请输入要保存路径的文件名\n");
scanf("%s",name);
FILE *fp=fopen(name,"wb");
if(fp==NULL)
    {
        printf("\n打开文件%s失败\n",name);
        exit(1);
    }
    fprintf(fp,"迷宫路径:\n");
    for(i=n-1;i>=0;i--)
    fprintf(fp,"<%d,%d>  ",s1[i],s2[i]);
    fclose(fp);
    printf("已成功保存于%s文件中",name);
}
void print2(seqstack &stack,smaze &maze,int &m,int &n)
{
    int s1[20],s2[20],i=0,j=0,n1,n2;
    while(stack.top!=-1)
    {
        pop(stack,s1[i]);
        pop(stack,s2[j]);
        n1=s1[i];
        n2=s2[j];
        maze[n1][n2].flag=2;
        i++;
        j++;
    }
    printf("以形象的矩阵形式输出迷宫路径 ·表示走过的痕迹 ⊙表示迷宫路径\n");
    printf("如下所示\n");
    for(i=0;i<=m+1;i++)
    {
        for(j=0;j<=n+1;j++)
        {
            if(maze[i][j].m1==1)printf("■");
            if(maze[i][j].m1==0&&maze[i][j].flag==0)
            printf("□");
            if(maze[i][j].m1==0&&maze[i][j].flag==1)
            printf("·");
            if(maze[i][j].m1==0&&maze[i][j].flag==2)
            printf("⊙");
        }
        printf("\n");
    }
}
void savefile(smaze maze,int &m,int &n)
{
    int i,j;
    char name[20];
    printf("输入迷宫:\n");
    for(i=1;i<=m;i++)
    for(j=1;j<=n;j++)
    {
        scanf("%d",&maze[i][j].m1);
        if(maze[i][j].m1==1)
        maze[i][j].flag=1;
        else
        maze[i][j].flag=0;
    }
    for(i=0;i<=m+1;i++)
    {
        maze[i][0].m1=1;
        maze[i][0].flag=1;//加两堵列墙
        maze[i][n+1].m1=1;
        maze[i][n+1].flag=1;
    }
    for(j=0;j<=n+1;j++)    //加两堵行墙
    {
        maze[0][j].flag=1;
        maze[m+1][j].flag=1;
        maze[0][j].m1=1;
        maze[m+1][j].m1=1;
    }
    printf("请输入保存的文件名:\n");
    scanf("%s",name);
    FILE *fp=fopen(name,"wt");
    if(fp==NULL)
    {
        printf("\n打开文件%s失败\n",name);
        exit(1);
    }
    for(i=0;i<=m+1;i++)
    {
        for(j=0;j<=n+1;j++)
        {
            if(maze[i][j].m1==1)
                printf("■");
            else
                printf("□");
        }
        printf("\n");
    }
    fprintf(fp,"迷宫路径:\n");
    for(i=1;i<=m;i++)
    {
        for(j=1;j<=n;j++)
        fprintf(fp,"%d ",maze[i][j].m1);
        fputc('\n',fp);
    }
    fclose(fp);
}
int print()
{
    int i;
    printf("输入执行的功能序号\n");
    scanf("%d",&i);
    return i;
}
int main()
{
    seqstack stack;
    inista(stack);
    int m,n,f;
    smaze maze;
    printf("1、从键盘输入迷宫\n");
    printf("2、从文件读出迷宫\n");
    printf("3、走迷宫\n");
    printf("4、以坐标形式输出迷宫路径\n");
    printf("5、以形象的矩阵形式表示路径\n");
    printf("0、退出走迷宫系统\n");
    while(f!=0)
    {
        f=print();
        switch(f)
        {
            case 0:
                printf("您已退出系统期待你的下次访问\n");
                break;
            case 1:
                printf("输入迷宫的行和列:\n");
                scanf("%d %d",&m,&n);
                savefile(maze,m,n);
                break;
            case 2:
                printf("输入迷宫的行和列:\n");
                scanf("%d %d",&m,&n);
                inread(maze,m,n);
                break;
            case 3:
                zumigong(stack,maze,m,n/*,s1,s2*/);
                break;
            case 4:
                print1(stack);
                break;
            case 5:
                print2(stack,maze,m,n);
                break;
            case 6:;
        }
    }
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值