AOJ-AHU-OJ-595 撒哈拉大冒险(栈)

本文深入探讨了如何通过一种创新的方法,解决在无限扩展的迷宫中寻找无限行走路径的问题。作者巧妙地运用了深度优先搜索(DFS)与剪枝技巧,不仅简化了复杂的数据结构处理,还成功避免了不必要的计算开销。通过实例分析和代码实现,详细解释了如何从迷宫的入口出发,遵循特定规则,确保在迷宫中无限前行的可能性。案例来源于安徽大学第五届ACM/ICPC程序设计竞赛现场赛,展示了在有限资源约束下解决问题的高效策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

撒哈拉大冒险
Time Limit: 1000 ms   Case Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 193   Submission Accepted: 14
Description
在撒哈拉大沙漠中,有一个神奇的迷宫,那是所有冒险,探索者都想要破解的圣地。
迷宫是由多块相同的子迷宫紧凑拼接而成,每一个子迷宫的规模为n*m,整个迷宫是无限大的。
大意为对于某一块迷宫,他的上下左右,都是拥有相同的布局的迷宫,而且方向一致,不可旋转。
这样便遗留下了一个问题,从入口出发之后,每次只能往上下左右四个方向移动,在不重复走同一个位置的前提下,整个迷宫是否可以无限走下去。
很多探险家便是在迷宫里累死的。请帮助cxlove判断迷宫是否是可以无限走下去。

Input
一个整数T,表示T组数据。(1<=T<=100)
对于每组数据:
两个整数n,m。(1<=n,m<=1000)
接下来n行,每行m个字符。描述整个迷宫。‘S’表示入口位置,‘.’表示该位置可以到达,‘#’表示该位置为障碍物,不可到达。

Output
对于每组数据,如果可以无限走下去,输出“YES”,否则输出“NO”。

Sample Input
Original Transformed
3
3 3
...
#S#
###
5 4
##.#
##S#
#..#
#.##
#..#
4 4
####
#..#
#S.#
####

Sample Output
Original Transformed
YES
YES
NO

Hint
Sample 1:
从入口出发,然后向走一步,由于第一行全部都为空地,所以可以一直往左或者往右走,便会无限移动下去。

Source
安徽大学第五届ACM/ICPC程序设计竞赛 现场赛

————————————————————害羞的分割线————————————————————
思路:一看到走迷宫,想起来DFS和BFS。但是选择哪个更好呢?直觉告诉我DFS+剪枝……咳咳,其实是因为刚做了HDU的Temper of the Bone啦!题目大致是迷宫可以无限拼接。我们把迷宫放在正当中,在它的上下左右增加复制的迷宫,看从中间的S点是否能走到其他的S点。
分析之后,我们发现1000×1000的图扩展成9张太浪费。于是精简成3张,留下右边和下边。该思路可行。但是写出来之后TLE了。
然后思考,是否不必走到另一个S点。毕竟走到另一个S点意味着很多很多耗时。询问之后得到了另一个思路,就是只走一张图,如果从S点出发后,「越界」走出这张图,之后坐标“改”成「非越界」,也就是模拟了走到另外一张完全相同的迷宫当中。
最后,考虑一下需要的数组。首先是栈sta[ ]、vis[ ](结构体),之后是地图mat[ ][ ]。重要变量:ox、oy,nx、ny。前者是相对坐标,后者是当前的绝对坐标。方法:(x Mod m + m)Mod m
代码如下:
//方法:栈+相对位置(模拟)
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}, l, r, sx, sy;
char mat[1010][1010];
const int IMP = 0x7f7f7f7f;//这是一个相当大的值,作为“不可能”访问到的值
typedef struct{
    int x, y;
}Node;//利用压缩的方法储存下标会遇到很多问题,比如负数下标,在本方法中是可能的
Node vis[1010][1010], sta[1000100];//vis[]数组保存已访问的下标。
bool dfs() {
    int top = -1;
    for(int i = 0; i < l; i++)
        for(int j = 0; j < r; j++)
            vis[i][j].x = vis[i][j].y = IMP;//初始化vis[]
    Node u;
    u.x = sx; u.y = sy;
    sta[++top] = vis[sx][sy] = u;//起点进栈
    while(top != -1) {
        u = sta[top--];//更新u为当前栈首,同时出栈
        for(int d = 0; d < 4; d++) {
            int nx = u.x + dx[d], ny = u.y + dy[d];//绝对位置
            int ox = (nx%l + l)%l, oy = (ny%r + r)%r;//相对位置
            if(mat[ox][oy] == '#')  continue;//相对位置是'#',无法在此处扩展迷宫
            if(vis[ox][oy].x == IMP) {//IMP表示没有访问过
                sta[++top].x = vis[ox][oy].x = nx;//进栈并储存该相对位置的绝对位置
                sta[top].y = vis[ox][oy].y = ny;
            }
            else if(vis[ox][oy].x != nx||vis[ox][oy].y != ny)  return true;//该相对位置已访问?有可能无限扩展!绝对位置不同?一定无限!
        }
    }
    return false;
}
int main() {
    int cas;
    scanf("%d", &cas);
    while(cas--) {
        scanf("%d%d", &l, &r);
        for(int i = 0; i < l; i++) {
            scanf("%s", mat[i]);
            for(int j = 0; j < r; j++) {
                if(mat[i][j] == 'S') {
                    sx = i;
                    sy = j;
                }
            }
        }//储存地图并记录起点位置
        if(dfs())  puts("YES");
        else  puts("NO");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值