一般迷宫问题的求解

来源:互联网 发布:java如何实现方法重载 编辑:程序博客网 时间:2024/05/17 01:33

1.问题描述:

以一个m*n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。

2.基本要求

(1)首先实现一个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。求得的通路以三元组(i,j,d)的形式输出。其中:(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。如,对于教材第50页图3.4所示的迷宫,输出一条通路为:(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2),…。
(2)编写递归形式的算法,求得迷宫中所有可能的通路。
(3)以方阵形式输出迷宫及其通路。

这里除了书上要求的利用栈完成遍历,还有一个利用队列完成遍历并进行路径回收的算法

1.用链栈完成图的遍历并保存路径

基本思路:

由于DFS是盲目的,所以没有办法知道那个点可能到达最后的终点。所以需要引入一个father指针,指向某一点的前驱。在最后找到目标点时,再利用其父节点进行回溯
对于每一个可能的点入栈之后,专门一个指针指向它的父节点。
然后在进行pop的操作时,不将任何一个点free掉,否则father指针将会变成野指针,无法完成作用。

基本结构

typedef struct Node2{    int x,y,d;    struct Node2 *next,*prior;    struct Node2 *father;//father指针是回溯用的}Node2;typedef struct{    Node2 *head;    Node2 *top;}LinkStack;

基本函数

初始化

LinkStack* Init(LinkStack *s){    s=(LinkStack*)malloc(sizeof(LinkStack*));    s->head=(Node2*)malloc(sizeof(Node2*));    s->top=s->head;    s->head->x=s->head->y=0;    s->head->next=s->head->prior=NULL;    s->head->father=s->head;    return s;}

Push操作

LinkStack* Push(LinkStack *s,Node2 *father,int x,int y){    Node2 *p=(Node2*)malloc(sizeof(Node2));    p->x=x;    p->y=y;    p->prior=s->top;    p->father=father;    p->next=NULL;    p->d=0;    s->top->next=p;    s->top=s->top->next;    return s;}

Pop操作

LinkStack* Pop(LinkStack *s){    Node2 *p=s->top;    s->top=p->prior;    //free(p);不能将p点free掉,否则无法完成回溯    return s;}

取栈顶元素

Node2* front(LinkStack *s,int &x,int &y){    Node2 *p=s->top;    x=p->x;    y=p->y;    return s->top;}

指针回溯

Node2* Back(LinkStack* s){    return s->top->father;}

判断栈是否空

bool isempty(LinkStack *s){    if(s->top==s->head)        return true;    return false;}

DFS部分

void dfs1(int x,int y,int tarx,int tary){    int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};    bool flag=false;    LinkStack* s=Init(s);    s=Push(s,s->head,x,y);//将起点压栈    visit[x][y]=false;    int dx,dy;    while(!isempty(s))    {        Node2 *father=front(s,dx,dy);        if(dx==tarx&&dy==tary){//达到终点,跳出循环            flag=true;            break;        }        s=Pop(s);        for(int i=0;i<4;++i){            dx+=dis[i][0];            dy+=dis[i][1];            if(check(dx,dy)){                s=Push(s,father,dx,dy);//可能到达的地方压栈                visit[dx][dy]=false;            }        }    }    if(!flag)        cout<<"没有通路"<<endl;    else{        int cnt=0;        Road rpp[maxn];        while(1)        {            if(s->top->x==x&&s->top->y==y){                rpp[cnt].x=x;                rpp[cnt].y=y;                rpp[cnt].d=s->top->d;                break;            }            Node2 *p=s->top->father;            if(s->top->x>p->x)//根据上一个点判断是哪个方向                s->top->father->d=3;            else if(s->top->x<p->x)                s->top->father->d=1;            else if(s->top->y>p->y)                s->top->father->d=2;            else                s->top->father->d=4;            rpp[cnt].x=s->top->x;//点的信息放入三元组            rpp[cnt].y=s->top->y;            rpp[cnt].d=s->top->d;            cnt++;            s->top=Back(s);        }        for(int i=cnt;i>=0;--i)            cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;    }}

递归完成图的遍历并输出所有通路

基本思想

要输出所有通路,其实就是把一个最简单的DFS添加一个DFS深度参数和一个路径保存数组。每次DFS都会更新数组,如果到达目标点,就可以把结果输出,然后去找下一个可能的路径。

基本结构

路径保存结构

typedef struct{    int x,y,d;}Road;

基本函数

方向判断

int dir(int x1,int y1,int x2,int y2){    if(x1<x2)        return 1;    else if(x1>x2)        return 3;    else if(y1<y2)        return 2;    else        return 4;}

输出路径

void out(int len){    char mp1[110][110];    for(int i=1;i<=n;++i){        for(int j=1;j<=m;++j)            mp1[i][j]=mp[i][j]+'0';    }    for(int i=0;i<=len;++i){        if(sol[i].d==1)            mp1[sol[i].x][sol[i].y]='|';        else if(sol[i].d==2)            mp1[sol[i].x][sol[i].y]='-';        else if(sol[i].d==3)            mp1[sol[i].x][sol[i].y]='|';        else            mp1[sol[i].x][sol[i].y]='-';    }    for(int i=1;i<=n;++i){        for(int j=1;j<=m;++j)            cout<<mp1[i][j];        cout<<endl;    }    cout<<endl;}

DFS部分

void dfs2(int x,int y,int tarx,int tary,int k)//添加一个参数k代表深度{    sol[k].x=x;//记录路径    sol[k].y=y;    sol[k-1].d=dir(sol[k-1].x,sol[k-1].y,sol[k].x,sol[k].y);//记录方向    if(x==tarx&&y==tary){        out(k);        return ;    }    visit2[x][y]=false;    int dx=x,dy=y;    for(int i=0;i<4;++i){        dx+=dis[i][0];        dy+=dis[i][1];        if(check2(dx,dy))            dfs2(dx,dy,tarx,tary,k+1);    }    visit2[x][y]=true;}

利用队列完成遍历和路径回溯

基本思想

利用队列当然就是BFS,只不过出队并不能真正的删除数据,而且也需要一个father指针指向其父节点,最后跳回起点,完成路径回溯

基本结构

队列元素

typedef struct{    int x,y;    int father;}Node;

队列

typedef struct{    Node data[maxn];    int l,r;}Queue;

路径记录

typedef struct{    int x,y,d;}Road;

BFS部分

void bfs(int x,int y,int tarx,int tary){    int pos;    Queue que;    Road rpp[maxn];    visit2[x][y]=false;    Node a;    bool flag=false;    que.l=que.r=0;    a.x=x;a.y=y;a.father=0;    que.data[que.r++]=a;    while(que.l!=que.r)//当队列不空    {        Node b=que.data[que.l];        if(b.x==tarx&&b.y==tary){            pos=que.l;            flag=true;            break;        }        que.l++;        for(int i=0;i<4;++i){//遍历四个方向            Node c;            int dx=dis[i][0];            int dy=dis[i][1];            c.x=b.x;            c.y=b.y;            c.x+=dx;            c.y+=dy;            c.father=que.l-1;            if(check2(c.x,c.y)){                que.data[que.r++]=c;                visit2[c.x][c.y]=false;  //及时进行标记            }        }    }    if(!flag)        cout<<"达不到出口"<<endl;    else{        int cnt=0;        while(1)//路径回溯        {            rpp[cnt].x=que.data[pos].x;            rpp[cnt].y=que.data[pos].y;            if(rpp[cnt].x==x&&rpp[cnt].y==y){//到达出发点                cnt++;                break;            }            int d=0,nextpos=que.data[pos].father;            Node k=que.data[nextpos];            if(que.data[pos].x>k.x)//根据上一个点判断是哪个方向                d=3;            else if(que.data[pos].x<k.x)                d=1;            else if(que.data[pos].y>k.y)                d=2;            else                d=4;            rpp[cnt].d=d;            pos=que.data[pos].father;//回溯            cnt++;        }        for(int i=cnt-1;i>=0;--i)            cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;    }}

全部代码

#include<bits/stdc++.h>using namespace std;const int maxn=1e4+10;typedef struct{    int x,y;    int father;}Node;typedef struct{    Node data[maxn];    int l,r;}Queue;typedef struct{    int x,y,d;}Road;typedef struct Node2{    int x,y,d;    struct Node2 *next,*prior;    struct Node2 *father;}Node2;typedef struct{    Node2 *head;    Node2 *top;}LinkStack;int mp[110][110];bool visit1[110][110];bool visit2[110][110];Road sol[maxn];int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};int m,n;LinkStack* Init(LinkStack *s){    s=(LinkStack*)malloc(sizeof(LinkStack*));    s->head=(Node2*)malloc(sizeof(Node2*));    s->top=s->head;    s->head->x=s->head->y=0;    s->head->next=s->head->prior=NULL;    s->head->father=s->head;    return s;}LinkStack* Push(LinkStack *s,Node2 *father,int x,int y){    Node2 *p=(Node2*)malloc(sizeof(Node2));    p->x=x;    p->y=y;    p->prior=s->top;    p->father=father;    p->next=NULL;    p->d=0;    s->top->next=p;    s->top=s->top->next;    return s;}LinkStack* Pop(LinkStack *s){    Node2 *p=s->top;    s->top=p->prior;    //free(p);不能将p点free掉,否则无法完成回溯    return s;}Node2* front(LinkStack *s,int &x,int &y){    Node2 *p=s->top;    x=p->x;    y=p->y;    return s->top;}Node2* Back(LinkStack* s){    return s->top->father;}bool isempty(LinkStack *s){    if(s->top==s->head)        return true;    return false;}void print() //输出图{    for(int i=1;i<=n;++i){        for(int j=1;j<=m;++j)            cout<<mp[i][j]<<" ";        cout<<endl;    }    cout<<endl;}bool check1(int x,int y) //防越界处理{    if(x>=1&&x<=n&&y>=1&&y<=m)        if(visit1[x][y])            return true;    return false;}bool check2(int x,int y) //防越界处理{    if(x>=1&&x<=n&&y>=1&&y<=m)        if(visit2[x][y])            return true;    return false;}void out(int len){    char mp1[110][110];    for(int i=1;i<=n;++i){        for(int j=1;j<=m;++j)            mp1[i][j]=mp[i][j]+'0';    }    for(int i=0;i<=len;++i){        if(sol[i].d==1)            mp1[sol[i].x][sol[i].y]='|';        else if(sol[i].d==2)            mp1[sol[i].x][sol[i].y]='-';        else if(sol[i].d==3)            mp1[sol[i].x][sol[i].y]='|';        else            mp1[sol[i].x][sol[i].y]='-';    }    for(int i=1;i<=n;++i){        for(int j=1;j<=m;++j)            cout<<mp1[i][j];        cout<<endl;    }    cout<<endl;}int dir(int x1,int y1,int x2,int y2){    if(x1<x2)        return 1;    else if(x1>x2)        return 3;    else if(y1<y2)        return 2;    else        return 4;}void dfs2(int x,int y,int tarx,int tary,int k){    sol[k].x=x;    sol[k].y=y;    sol[k-1].d=dir(sol[k-1].x,sol[k-1].y,sol[k].x,sol[k].y);    if(x==tarx&&y==tary){        out(k);        return ;    }    visit2[x][y]=false;    int dx=x,dy=y;    for(int i=0;i<4;++i){        dx+=dis[i][0];        dy+=dis[i][1];        if(check2(dx,dy))            dfs2(dx,dy,tarx,tary,k+1);    }    visit2[x][y]=true;}void bfs(int x,int y,int tarx,int tary){    int pos;    Queue que;    Road rpp[maxn];    visit2[x][y]=false;    Node a;    bool flag=false;    que.l=que.r=0;    a.x=x;a.y=y;a.father=0;    que.data[que.r++]=a;    while(que.l!=que.r)//当队列不空    {        Node b=que.data[que.l];        if(b.x==tarx&&b.y==tary){            pos=que.l;            flag=true;            break;        }        que.l++;        for(int i=0;i<4;++i){//遍历四个方向            Node c;            int dx=dis[i][0];            int dy=dis[i][1];            c.x=b.x;            c.y=b.y;            c.x+=dx;            c.y+=dy;            c.father=que.l-1;            if(check2(c.x,c.y)){                que.data[que.r++]=c;                visit2[c.x][c.y]=false;  //及时进行标记            }        }    }    if(!flag)        cout<<"达不到出口"<<endl;    else{        int cnt=0;        while(1)//路径回溯        {            rpp[cnt].x=que.data[pos].x;            rpp[cnt].y=que.data[pos].y;            if(rpp[cnt].x==x&&rpp[cnt].y==y){//到达出发点                cnt++;                break;            }            int d=0,nextpos=que.data[pos].father;            Node k=que.data[nextpos];            if(que.data[pos].x>k.x)//根据上一个点判断是哪个方向                d=3;            else if(que.data[pos].x<k.x)                d=1;            else if(que.data[pos].y>k.y)                d=2;            else                d=4;            rpp[cnt].d=d;            pos=que.data[pos].father;//回溯            cnt++;        }        for(int i=cnt-1;i>=0;--i)            cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;    }}void dfs1(int x,int y,int tarx,int tary){    bool flag=false;    LinkStack* s=Init(s);    s=Push(s,s->head,x,y);//将起点压栈    visit1[x][y]=false;    int dx,dy;    while(!isempty(s))    {        Node2 *father=front(s,dx,dy);        if(dx==tarx&&dy==tary){//达到终点,跳出循环            flag=true;            break;        }        s=Pop(s);        for(int i=0;i<4;++i){            dx+=dis[i][0];            dy+=dis[i][1];            if(check1(dx,dy)){                s=Push(s,father,dx,dy);//可能到达的地方压栈                visit1[dx][dy]=false;            }        }    }    if(!flag)        cout<<"没有通路"<<endl;    else{        int cnt=0;        Road rpp[maxn];        while(1)        {            if(s->top->x==x&&s->top->y==y){                rpp[cnt].x=x;                rpp[cnt].y=y;                rpp[cnt].d=s->top->d;                break;            }            Node2 *p=s->top->father;            if(s->top->x>p->x)//根据上一个点判断是哪个方向                s->top->father->d=3;            else if(s->top->x<p->x)                s->top->father->d=1;            else if(s->top->y>p->y)                s->top->father->d=2;            else                s->top->father->d=4;            rpp[cnt].x=s->top->x;//点的信息放入三元组            rpp[cnt].y=s->top->y;            rpp[cnt].d=s->top->d;            cnt++;            s->top=Back(s);        }        for(int i=cnt;i>=0;--i)            cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;    }}int main(){    freopen("in.txt","r",stdin);    int x,y,tarx,tary;    memset(visit1,true,sizeof(visit1));    memset(visit2,true,sizeof(visit2));    memset(mp,1,sizeof(mp));    cout<<"输入n和m,起点坐标和目标点坐标"<<endl;    cin>>n>>m>>x>>y>>tarx>>tary;    cout<<"输入图"<<endl;    for(int i=1;i<=n;++i)        for(int j=1;j<=m;++j){            cin>>mp[i][j];            if(mp[i][j]){                visit2[i][j]=false;                visit1[i][j]=false;            }    }        dfs1(x,y,tarx,tary);        cout<<endl;        dfs2(x,y,tarx,tary,0);    return 0;}