解决迷宫回溯问题

时间:2017-10-24 05:25:41

标签: c backtracking maze

我正在尝试使用C中的回溯来解决迷宫。要解决迷宫以下规则:

  • 你从S的位置开始,需要走向E
  • 你只能继续......'路径
  • 转换所有'。'进入'#'包括S和E

输入由m×n矩阵组成: 输入示例:

11 11
+-+-+-+-+-+
S.|...|...|
+.+.+.+-+.+
|.|.|.....|
+.+-+-+-+.+
|...|.|...|
+.+.+.+.+-+
|.|...|.|.|
+.+-+.+.+.+
|...|.....E
+-+-+-+-+-+

预期解决方案:

+-+-+-+-+-+
##|...|...|
+#+.+.+-+.+
|#|.|.....|
+#+-+-+-+.+
|###|.|...|
+.+#+.+.+-+
|.|###|.|.|
+.+-+#+.+.+
|...|######
+-+-+-+-+-+

我正在努力解决这个问题,但由于某种原因,一旦我到达了迷宫中的某个点,我就无法再回到过去。我只是向所有方向前进它看到了一个'

我的想法是从S的位置开始,并在每个递归步骤使用我们的旧位置。 如果我所看到的位置是'我将从我所站立的位置向所有方向前进。'如果这一点不是我原来的立场。

当我回到原点时,我也认为当我遇到一个十字路口时,我遇到了问题。例如:

+-+-+-+-+-+
##|...|...|
+#+.+.+-+.+
|#|.|.....|
+#+-+-+-+.+
|0##|.|...|
+.+#+.+.+-+
|.|###|.|.|
+.+-+#+.+.+
|..1|######
+-+-+-+-+-+

想象一下,我处于0位置。我从1回溯到#变成了#。'。我怎样才能说出你有2个可能性回去,但是你应该停下来吗?

我的代码:

#include <stdio.h>
#include <stdlib.h>

void *safeMalloc(int n) {
    void *p = malloc(n);
    if (p == NULL) {
        printf("Error: malloc(%d) failed. Out of memory?\n", n);
        exit(EXIT_FAILURE);
    }
    return p;
}


char ** readMatrix(int m,int n,int* startI,int* startJ,int* endI,int* endJ){
    char **arr = safeMalloc(m*sizeof(char *));
    int row;
    for (row=0; row < m; row++) {
        arr[row] = safeMalloc(n*sizeof(char));
    }
    int i,j;
    for(i=0;i<m;i++){
        for(j=0;j<m;j++){
            scanf(" %c",&arr[i][j]);
            if(arr[i][j]=='S'){
                *startI=i;
                *startJ=j;
            }
            if(arr[i][j]=='E'){
                *endI=i;
                *endJ=j;
            }
        }
        getchar();
    }

    return arr;
}

void printNumber(char **arr,int m,int n){
    int i,j;
    for(i=0;i<m;i++){
        for(j=0;j<n;j++){
            printf("%c", arr[i][j]);
        }
        printf("\n");
    }
}

void findPath(char** arr,int m,int n,int startI,int startJ,int endI,int endJ,int oldI,int oldJ){
    int i=startI,j=startJ;
    int stepsPossible=4;
            //going up
           if(i-1>=0){
                if((arr[i-1][j]=='.') && ((i-1!=oldI) || (j!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i-1,j,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }
           //going right

           if(j+1<n){
                if((arr[i][j+1]=='.') && ((i!= oldI) || (j+1!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j+1,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }
           //going left
           if(j-1>=0){
                if((arr[i][j-1]=='.') && ((i!= oldI) || (j-1!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j-1,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }

           //going down
            if(i+1<m){
                if((arr[i+1][j]=='.') && ((i+1!= oldI) || (j!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i+1,j,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }
        //if the next block is E then we can stop.
           if((arr[i-1][j]=='E') || (arr[i][j+1]=='E') || (arr[i][j-1]=='E') || (arr[i+1][j]=='E')){
                if(arr[i-1][j]=='E'){
                    arr[i-1][j]='#';
                }

                if(arr[i][j+1]=='E'){
                    arr[i][j+1]='#';
                }

                if(arr[i][j-1]=='E'){
                    arr[i][j-1]='#';
                }

                if(arr[i+1][j]=='E'){
                    arr[i+1][j]='#';
                }
                return;
            }


            if(stepsPossible==0){
                if(arr[i-1][j]=='#'){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i-1,j,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }

                if(arr[i][j+1]=='#' ){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j+1,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }

                if(arr[i][j-1]=='#' ){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j-1,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }

                if(arr[i+1][j]=='#' ){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i+1,j,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }
            }
}


int main()
{
    int m,n;
    scanf("%d %d",&m,&n);
    int startI,startJ,endI,endJ;
    char** arr;
    arr=readMatrix(m,n,&startI,&startJ,&endI,&endJ);
    findPath(arr,m,n,startI,startJ,endI,endJ,startI,startJ);
    printNumber(arr,m,n);
    return 0;
}

2 个答案:

答案 0 :(得分:1)

我建议使用BFS,因为

  • BFS将找到最短的解决方案。
  • DFS非常糟糕地处理某些迷宫。

以下是您的案例的简短描述:

  1. 在迷宫中找到'S'并将其添加到队列中
  2. 当队列不为空时,检查队列中的get元素。 用“#”替换元素。如果元素是E,那么你就完成了。检查元素的邻居(向上,向下,向右),如果它们是“。”,则添加到队列中。
  3. 如果队列为空且未找到E,则没有从S到E的直接路径

答案 1 :(得分:1)

正确使用返回值,因为您可以利用它来简化逻辑。在findPath上为错误情况选择不同的返回值(需要回溯)和成功案例(到达终点)。

现在,您可以无条件地将#置于该函数的开头,并最终无条件地重置回.以进行回溯案例。

也无需计算可能的方向,只检查某些呼叫是否恢复成功。

也不需要一遍又一遍地编写边界检查,如果你只是在函数的开头检查它们就可以通过无效的坐标而没有任何问题。

bool findPath(char** arr, size_t sx, size_t sy, int x, int y) {
    if (x < 0 || x >= sx || y < 0 || y >= sy) return false;
    if (arr[x][y] == 'E') {
        are[x][y] = '#';
        return true;
    }
    if (arr[x][y] != '.') return false;
    arr[x][y] = '#';
    bool success = findPath(arr, sx, sy, x-1, y) ||
        findPath(arr, sx, sy, x+1, y) ||
        findPath(arr, sx, sy, x, y-1) ||
        findPath(arr, sx, sy, x, y+1);
    if (!success) arr[x][y] = '.';
    return success;
}

回溯算法的实现通常都遵循相同的模式:

  1. 尝试轻易拒绝或接受当前的解决方案。
  2. 修改当前解决方案。
  3. 尝试各种变化。
  4. 如果不成功,请清理修改。