uva10384 The Wall Pusher 推门游戏

问题

从S出发,每次往东南西北四个方向前进。 如果前方有墙壁,游戏者可以往前推移一格。 如果有连续两堵墙(包含)以上的墙,或者是游戏区域边界上的墙 则推不动。
求最少步数走出迷宫 4行6列,多解时任意输出一格移动序列即可

思路

  • 迷宫怎么存储
    用三元组表示一堵墙
    数组wallX[y][x1][x2] 表示第y=y时 从x1与x2之间有一堵墙
    数组wallY[x][y1][y2] 表示第x=x时 从y1与y2之间有一堵墙
    数组wallX wallY 就能刻画迷宫的形状

  • 起点S(x, y) 如何判断应走出迷宫
    行y[1, 4] 列x[1,6]; 增加虚拟行y=0、5 虚拟列x=0、7
    如果能走到 (0, *)|(7, *)|(*,0)|(*,5) 其中任意一个位置 即走出迷宫

  • 如何保证步数最少
    解决迷宫问题 一般都用递归;
    用深度优先DFS获取的第一条路径不一定是最短的;
    用广度优先BFS,第一条路径肯定是最短的 但需要维护一个队列,存储每走一步后的迷宫状态,很占空间;
    如果用迭代加深搜素算法IDA*则能有效解决上述问题,即限定BFS的深度,并依次递增知道找到问题的解
for(depth=1; ;depth++)
    DFS(S0); // 限制递归深度最大为depth 
    if find soulation:
        break;  

测试案例 & 结果

输入:
x 1 6 7
x 2 0 1
x 2 2 3
x 2 4 5
x 2 6 7
x 3 0 1
x 3 1 2
x 3 2 3
x 3 4 5
x 3 5 6
x 3 6 7
x 4 1 2
x 4 3 4
x 4 5 6
y 1 0 1
y 1 1 2
y 1 3 4
y 1 4 5
y 2 0 1
y 2 2 3
y 2 3 4
y 2 4 5
y 3 0 1
y 3 1 2
y 3 2 3
y 3 4 5
y 4 0 1
y 4 1 2
y 4 2 3
y 4 4 5
y 5 0 1
y 5 2 3
y 5 3 4
y 5 4 5
y 6 0 1
y 6 3 4
y 6 4 5
0 0 0 0

打印图像:
--------
@@_@_@_@_@_@_@@@
@@_@@@_@_@@@@|@@
@|@@_|_@_|_@@|@@
@|_|_|@@@|_|_|@@
@@_|_@_|_@_|_@@@
@@@@@@@@@@@@@@@@
--------

行走路径:
(2 3)
(2 2)
(3 2)
(3 3)
(4 3)
(4 4)
(5 4)
(6 4)
(6 3)
(6 2)
(5 2)
(5 1)
(4 1)
(3 1)
(2 1)
(1 1)
(0 1)

代码

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

#define MaxDepth 100

//当前允许递归的最大深度
int curMaxDepth;

//保存行走路径
int ansX[MaxDepth], ansY[MaxDepth];

//保存迷宫墙的信息
int wallX[6][8][8], wallY[8][6][6];

// 行走方向 东 南 西 北
int dirX[]={1, 0 , -1, 0}, dirY[]={ 0, 1, 0, -1};

// x、y的最大值
int maxX=7, maxY=5;

// 起点
int beginX=2, beginY=3;


// 打印图像
void showWall()
{
    int i,j;
    printf("--------\n");
    for(j=0; j<6; j++)
    {
        for(i=0;i<8;i++)
        {
            if( wallX[j][i][i+1]==1 && wallY[i][j][j+1]==1 )
                printf("_|");
            else if ( wallX[j][i][i+1]==1 && wallY[i][j][j+1]==0 )
                printf("@|");
            else if ( wallX[j][i][i+1]==0 && wallY[i][j][j+1]==1 )
                printf("_@");
            else if ( wallX[j][i][i+1]==0 && wallY[i][j][j+1]==0 )
                printf("@@");
        }
        printf("\n");
    }
    printf("--------\n");
}

// 找第step步的解
int find(int step)
{
    int x, y, nextX, nextY, i, j, k, wallMoved;
    x = ansX[step-1];
    y = ansY[step-1];

    // no solution;
    if( step > curMaxDepth ) return 0;

    // 四个方向 进行尝试
    for(i=0; i<4; i++)
    {
        nextX = x+dirX[i];
        nextY = y+dirY[i];
        wallMoved = 0;

        // 有墙 && (墙后还是墙||是边界墙) 无解
        if( wallX[y][x][nextX] && ( wallX[y][nextX][nextX+dirX[i]] || nextX==0 || nextX==7) )
            continue;

        if ( wallY[x][y][nextY] && ( wallY[x][nextY][nextY+dirY[i]] || nextY==0 || nextY==5) )
            continue;

        //  左|右 移动墙壁
        if( dirX[i] != 0  && wallX[y][x][nextX])
        {
            wallMoved = 1;
            wallX[y][x][nextX] = 0;
            wallX[y][nextX][x] = 0;
            wallX[y][nextX][nextX+dirX[i]] = 1;
            wallX[y][nextX+dirX[i]][nextX] = 1;
        }

        // 上|下 移动墙壁
        if( dirY[i] != 0 && wallY[x][y][nextY])
        {
            wallMoved = 1;
            wallY[x][y][nextY] = 0;
            wallY[x][nextY][y] = 0;
            wallY[x][nextY][nextY+dirY[i]] = 1;
            wallY[x][nextY+dirY[i]][nextY] = 1;
        }

        //保存 移动后的坐标
        ansX[step]=nextX;
        ansY[step]=nextY;

        // find solution
        if ( nextX==0 || nextX==7 || nextY==0 || nextY==5 )
            return 1;

        // 继续递归求解
        if ( find(step+1) )
            return 1;


        // 恢复墙壁
        if( wallMoved ==1 )
        {
            if ( dirX[i] != 0 )
            {
                wallX[y][x][nextX] = 1;
                wallX[y][nextX][x] = 1;
                wallX[y][nextX][nextX+dirX[i]] = 0;
                wallX[y][nextX+dirX[i]][nextX] = 0;
            }

            if(dirY[i] != 0 )
            {

                wallY[x][y][nextY] = 1;
                wallY[x][nextY][y] = 1;
                wallY[x][nextY][nextY+dirY[i]] = 0;
                wallY[x][nextY+dirY[i]][nextY] = 0;
            }
        }
    }
    return 0;
}

int alg10384()
{
    char flag[8];
    int i, j, a, b, c;
    while(scanf("%s %d %d %d", flag, &a, &b, &c )==4)
    {

        if (a==0&&b==0&&c==0) break;

        if(flag[0]=='x'||flag[0]=='X')
        {
            wallX[a][b][c]=1;
            wallX[a][c][b]=1;
        }

        if(flag[0]=='y'||flag[0]=='Y')
        {
            wallY[a][b][c]=1;
            wallY[a][c][b]=1;
        }
    }

    showWall();

    ansX[0]=beginX;
    ansY[0]=beginY;

    for(curMaxDepth=1; curMaxDepth <= MaxDepth; curMaxDepth++)
    {
        if( find(1) )
            break;
    }

    for(i=0; i<=curMaxDepth; i++)
    {
        printf("%d %d\n", ansX[i], ansY[i]);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值