首页 > 试题广场 >

迷宫

[编程题]迷宫
  • 热度指数:2211 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}给定一个 \mathrm{n \times m} 的迷宫,迷宫由 "#" 与"." 两种字符组成。其中 "#" 代表障碍物,"." 表示空地。迷宫中还有一个起点 "S" 和一个终点 "E" ,它们都可以视为空地。

\hspace{15pt}由于近期迷宫发生了塌方,导致起点和终点之间可能并不连通。幸运的是,你拥有一种超能力——在迷宫中移动时(移动方向为上、下、左、右四个方向之一),可以在当前位置朝任一方向(上、下、左、右四个方向之一)释放激光。激光能够清除该方向上所有的障碍物,并且这种超能力至多只能使用一次。

\hspace{15pt}现在,你需要判断是否能利用这种超能力成功从起点到达终点。

输入描述:
\hspace{15pt}第一行给定两个整数 \mathrm{n,m}(\mathrm{2 \leqq n,m \leqq 1000}) ,分别表示迷宫的行数和列数。
\hspace{15pt}下面 \mathrm{n} 行,每行 \mathrm{m} 个字符,描述迷宫的具体布局。字符只包含 "#"、"."、"S" 和 "E",并且起点与终点有且仅有一个。


输出描述:
\hspace{15pt}能够到达终点输出 \mathrm{YES} ;否则输出 \mathrm{NO}
示例1

输入

4 5
.####
S####
.####
.E###

输出

YES
示例2

输入

4 5
..###
S####
#####
##.E#

输出

YES

说明

显然可以从起点出发,到达\mathrm{(1,2)}处并向下方使用超能力,此时可以从起点到达终点。
示例3

输入

4 5
..###
S####
#####
###E#

输出

NO
#include <iostream>
#include <utility>
#include <vector>
#include <queue>
#include <array>
using namespace std;

int bfs(vector<string>& grid,int sx,int sy,int n,int m,int& minx,int& miny,int& maxx,int& maxy)
{
    queue<pair<int,int>> que;
    que.push(make_pair(sx, sy));
    minx=min(minx,sx);
    miny=min(miny,sy);
    maxx=max(maxx,sx);
    maxy=max(maxy,sy);
    grid[sx][sy]='#';
    array<int,4> decx={-1,1,0,0};
    array<int,4> decy={0,0,-1,1};
    while(!que.empty())
    {
        pair<int,int> cur=que.front();
        que.pop();
        for(int i=0;i<4;++i)
        {
            int x=cur.first+decx[i];
            int y=cur.second+decy[i];
            if(x<0||x>=n||y<0||y>=m)
            {
                continue;
            }
            if(grid[x][y]=='#')
            {
                continue;
            }
            if(grid[x][y]=='E')
            {
                return 1;
            }
            if(grid[x][y]=='.')
            {
                que.push(make_pair(x,y));
                minx=min(minx,x);
                miny=min(miny,y);
                maxx=max(maxx,x);
                maxy=max(maxy,y);
                grid[x][y]='#';
            }
        }
    }
    return 0;
}

int main() {
    int n,m;
    cin>>n>>m;
    vector<string> grid(n);
    int sx,sy,ex,ey;
    int sminx=n,sminy=m,smaxx=0,smaxy=0;
    int eminx=n,eminy=m,emaxx=0,emaxy=0;
    for(int i=0;i<n;++i)
    {
        cin>>grid[i];
        for(int j=0;j<m;++j)
        {
            if(grid[i][j]=='S')
            {
                sx=i;
                sy=j;
            }
            if(grid[i][j]=='E')
            {
                ex=i;
                ey=j;
            }
        }
    }
    if(sx==ex||sy==ey)
    {
        cout<<"YES"<<endl;
        return 0;
    }
    int is_end=bfs(grid,sx,sy,n,m,sminx,sminy,smaxx,smaxy);
    if(is_end==1)
    {
        cout<<"YES"<<endl;
        return 0;
    }
    bfs(grid,ex,ey,n,m,eminx,eminy,emaxx,emaxy);
    if(ex>sx&&ey>sy)//E在S右下方
    {
        if(smaxx>=eminx-1||smaxy>=eminy-1)
        {
            cout<<"YES"<<endl;
            return 0;
        }
    }
    else if(ex>sx&&ey<sy)//E在S的左下方
    {
        if(smaxx>=eminx-1||emaxy>=sminy-1)
        {
            cout<<"YES"<<endl;
            return 0;
        }
    }
    else if(ex<sx&&ey<sy)//E在S的左上方
    {
        if(emaxx>=sminx-1||emaxy>=sminy-1)
        {
            cout<<"YES"<<endl;
            return 0;
        }
    }
    else//ex<sx,ey>sy即E在S的右上方或者某一个维度相等
    {
        if(emaxx>=sminx-1||smaxy>=eminy-1)
        {
            cout<<"YES"<<endl;
            return 0;
        }
    }
    cout<<"NO"<<endl;
    return 0;
}
如果说起点坐标和终点坐标在某个维度相等就一定能够到达,如果不等就先对起始坐标进行bfs,如果找到了终点就直接输出"YES",如果没有找到再对终点坐标进行bfs,每次bfs维护一个边界坐标,再根据E的坐标相对于S的位置进行判断,如果说差一格就可以直接输出”YES",反之“NO"
发表于 2025-12-14 14:49:51 回复(0)
参考了9.16的题解,求从S出发和从E出发的遍历格子的矩形范围,之后判断两个矩形是否相交,因为激光判断除相交外相邻亦可
#include <bits/stdc++.h>
#include <utility>
using namespace std;

void bfs(int startX, int startY, vector<vector<char>>& g, vector<vector<bool>>& used, vector<int>& dir)
{
    int n = g.size(), m = g[0].size();
    queue<pair<int, int>> qp;
    qp.push(make_pair(startX, startY));
    used[startX][startY] = true;

    vector<int> dx = { 1, -1, 0, 0 };
    vector<int> dy = { 0, 0, 1, -1 };

    while (qp.size())
    {
        pair<int, int> loc = qp.front();
        qp.pop();

        dir[0] = min(loc.first, dir[0]);
        dir[1] = max(loc.first, dir[1]);
        dir[2] = min(loc.second, dir[2]);
        dir[3] = max(loc.second, dir[3]);

        for (int i = 0; i < 4; i++)
        {
            int nx = loc.first + dx[i], ny = loc.second + dy[i];
            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if (used[nx][ny] || g[nx][ny] == '#') continue;
            used[nx][ny] = true;
            qp.push(make_pair(nx, ny));
        }
    }
}

int main() {
    int n, m;
    cin >> n >> m;

    vector<vector<char>> g(n, vector<char>(m));
    vector<vector<bool>> usedS(n, vector<bool>(m, false));
    vector<vector<bool>> usedE(n, vector<bool>(m, false));
    int startX, startY, endX, endY;

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cin >> g[i][j];
            if (g[i][j] == 'S')
            {
                startX = i;
                startY = j;
            }
            else if (g[i][j] == 'E')
            {
                endX = i;
                endY = j;
            }
        }
    }

    vector<int> dirS = { startX, startX, startY, startY };
    vector<int> dirE = { endX, endX, endY, endY };

    bfs(startX, startY, g, usedS, dirS);
    bfs(endX, endY, g, usedE, dirE);

    bool ans = false;
    if (dirS[0] <= dirE[1] + 1 && dirE[0] <= dirS[1] + 1) ans = true;
    if (dirS[2] <= dirE[3] + 1 && dirE[2] <= dirS[3] + 1) ans = true;

    if (ans) puts("YES");
    else puts("NO");

    return 0;
}



发表于 2025-12-11 12:44:00 回复(0)
BFS给我干蒙了,好在最后通过了
#include <stdio.h>
//看来还是得用BFS啊,深度没那么大
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//看来你的思路对了,只需要把dfs改成bfs就行了
int n, m;
int S_x, S_y, E_x, E_y;
int E_min_x, E_max_x, E_min_y, E_max_y;
bool E_to_S = false;
bool S_to_E = false;  // 添加这个全局标志
// 使用BFS替代DFS
void bfs(char** migong, int start_x, int start_y, bool is_E_to_S) {
    // 简单的队列实现
    int* queue_x = malloc(n * m * sizeof(int));
    int* queue_y = malloc(n * m * sizeof(int));
    int front = 0, rear = 0;
    
    // 入队起点
    queue_x[rear] = start_x;
    queue_y[rear] = start_y;
    rear++;
    migong[start_x][start_y] = '#';
    
    while (front < rear) {
        int x = queue_x[front];
        int y = queue_y[front];
        front++;
        
        if (is_E_to_S) {
            // E到S的BFS
            if (x == S_x && y == S_y) {
                printf("YES\n");
                E_to_S = true;
                free(queue_x);
                free(queue_y);
                return;
            }
            
            // 更新E连通块边界
            if (x-1 < E_min_x && x-1 >= 0) E_min_x = x-1;
            if (x+1 > E_max_x && x+1 < n) E_max_x = x+1;
            if (y-1 < E_min_y && y-1 >= 0) E_min_y = y-1;
            if (y+1 > E_max_y && y+1 < m) E_max_y = y+1;
        } else {
            // S到E范围的检查
            if ((x >= E_min_x && x <= E_max_x) || (y >= E_min_y && y <= E_max_y)) {
                printf("YES\n");
                S_to_E = true;  // 设置标志
                free(queue_x);
                free(queue_y);
                return;
            }
        }
        
        // 四个方向
        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};
        
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            
            if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
                if (is_E_to_S) {
                    if (migong[nx][ny] == '.' || migong[nx][ny] == 'S') {
                        migong[nx][ny] = '#';
                        queue_x[rear] = nx;
                        queue_y[rear] = ny;
                        rear++;
                    }
                } else {
                    if (migong[nx][ny] == '.' || migong[nx][ny] == 'E') {
                        migong[nx][ny] = '#';
                        queue_x[rear] = nx;
                        queue_y[rear] = ny;
                        rear++;
                    }
                }
            }
        }
    }
    
    free(queue_x);
    free(queue_y);
}

int main() {
    scanf("%d %d", &n, &m);
    char** migong = malloc(sizeof(char*) * n);
    char** migong_copy = malloc(sizeof(char*) * n); // 备份迷宫
    
    for (int i = 0; i < n; i++) {
        migong[i] = malloc(sizeof(char) * (m + 1));
        migong_copy[i] = malloc(sizeof(char) * (m + 1));
        scanf("%s", migong[i]);
        // 备份迷宫
        for (int j = 0; j < m; j++) {
            migong_copy[i][j] = migong[i][j];
        }
        migong_copy[i][m] = '\0';
    }
    
    // 获取S和E的坐标
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (migong[i][j] == 'S') {
                S_x = i;
                S_y = j;
            }
            if (migong[i][j] == 'E') {
                E_x = i;
                E_y = j;
                // 初始化E连通块边界
                E_min_x = E_x - 1 >= 0 ? E_x - 1 : 0;
                E_max_x = E_x + 1 < n ? E_x + 1 : E_x;
                E_min_y = E_y - 1 >= 0 ? E_y - 1 : 0;
                E_max_y = E_y + 1 < m ? E_y + 1 : E_y;
            }
        }
    }
    
    migong[E_x][E_y] = '.';
    bfs(migong, E_x, E_y, true); // E到S的BFS
    
    if (!E_to_S) {
        // 恢复迷宫状态
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                migong_copy[i][j] = migong_copy[i][j]; // 使用备份
            }
        }
        bfs(migong_copy, S_x, S_y, false); // S到E范围的BFS
        if (!S_to_E) {
            printf("NO\n");
        }
    }
    
    // 释放内存
    for (int i = 0; i < n; i++) {
        free(migong[i]);
        free(migong_copy[i]);
    }
    free(migong);
    free(migong_copy);
    
    return 0;
}



发表于 2025-10-14 12:16:47 回复(0)
用的题解里的方法,优化了一点点,代码长是因为想节省点空间,把visted数组拖出来变成全局变量了,因此写了两个bfs,本质上就是cv一下就行,改改变量名字就是两个bfs了
#include <iostream>
#include <queue>
#include <utility>
#include <vector>
using namespace std;
vector<string> grid;
vector<vector<bool>> visted_s;
vector<vector<bool>> visted_e;
int move_[5] = {1,0,-1,0,1};

void bfs_s(int i, int j)
{
    
    int n = grid.size();
    int m = grid[0].size();
    visted_s[i][j] = true;
    queue<pair<int, int>> que;
    que.emplace(i,j);

    while (!que.empty()) {
        auto [x,y] = que.front();
        que.pop();
        for (int k=0; k<4; ++k)
        {
            int nx = x + move_[k];
            int ny = y + move_[k+1];
            if (nx>=0 && nx<n && ny>=0 && ny<m && grid[nx][ny] == '.'
                && !visted_s[nx][ny])
            {
                visted_s[nx][ny] = true;
                que.emplace(nx,ny);
            }
        }
    }

}


void bfs_e(int i, int j)
{
    
    int n = grid.size();
    int m = grid[0].size();
    visted_e[i][j] = true;
    queue<pair<int, int>> que;
    que.emplace(i,j);

    while (!que.empty()) {
        auto [x,y] = que.front();
        que.pop();
        for (int k=0; k<4; ++k)
        {
            int nx = x + move_[k];
            int ny = y + move_[k+1];
            if (nx>=0 && nx<n && ny>=0 && ny<m && grid[nx][ny] == '.'
                && !visted_e[nx][ny])
            {
                visted_e[nx][ny] = true;
                que.emplace(nx,ny);
            }
        }
    }

}



int main() {
    int n,m;
    cin >> n >> m;
    int start_r,start_c,end_r,end_c;
    grid.resize(n);
    visted_s.resize(n,vector<bool>(m,false));
    visted_e.resize(n,vector<bool>(m,false));
    for (int i=0; i<n; ++i)
    {
        cin >> grid[i];
        for(int j=0; j<m; ++j)
        {
            if(grid[i][j] == 'S')
            {
                start_r = i;
                start_c = j;
            }
            else if (grid[i][j] == 'E') {
                end_r = i;
                end_c = j;
            }
        }
    }

    //如果从起点的bfs就可以到达终点,那么就直接返回yes
    bfs_s(start_r, start_c);
    if (visted_s[end_r][end_c])
    {
        cout << "YES";
        return 0;
    }

    bfs_e(end_r, end_c);

    //包括start_r start_c在内所有visted_s里的为true的点都可以发射激光
    //遍历visted_s每个可以发射激光的位置
    vector<bool> row(n);
    vector<bool> col(m);
    for (int i=0; i<n; i++)
    {
        for (int j=0; j<m; ++j)
        {
            if (visted_s[i][j])
            {
                row[i] = true;
                col[j] = true;
            }
        }
    }

    bool ans = false;
    for (int i=0; i<n; ++i)
    {
        for (int j=0; j<m; ++j)
        {
            //终点可以到达的点,以这个点为中心,上下三行,左右三列
            //只要能被激光射到,那就能通过一次激光到达。
            if (visted_e[i][j])
            {
                for (int t=-1; t<=1; ++t)
                {
                    int next_r = i+t;
                    int next_c = j+t;
                    if ((next_r>=0 && next_r<n && row[next_r]) ||
                        (next_c>=0 && next_c<m && col[next_c])
                        )
                    {
                        ans = true;
                        break;
                    }
                }

                
            }
        }
    }

    if (ans)
    {
        cout << "YES";
    }
    else {
        cout << "NO";
    }
}



发表于 2025-09-11 17:22:14 回复(0)
package main

/*
   BFS
   分别从S、E出发,把可达位置分别标记为S、E
   遍历过程中判断是否有同一行/列或相邻行/列内既含有S,也含有E
*/

import (
	"fmt"
)

type Node struct {
	i, j int
}

var nS, nE, nH byte = byte('S'), byte('E'), byte('#')

func isAdjoin(a, b int) bool { // 是否相邻
    if a > b{
        return a - b <= 1
    }
    return b - a <= 1
}

func main() {
	var n, m int
	fmt.Scanf("%d %d", &n, &m)
	var k = make([][]byte, n)
	var s string
	var S_i, S_j, E_i, E_j int
	for i := 0; i < n; i++ {
		fmt.Scanf("%s", &s)
		k[i] = make([]byte, m)
		k[i] = []byte(s)
		for j := 0; j < m; j++ {
			if k[i][j] == nS {
				S_i = i
				S_j = j
			}
			if k[i][j] == nE {
				E_i = i
				E_j = j
			}
		}
	}
	if isAdjoin(S_i, E_i) || isAdjoin(S_j, E_j) { // 相邻或同行或同列
		fmt.Println("YES")
		return
	}

    var si, sj = make([]bool, n),make([]bool, m) // 记录某行某列是否S可达 
    si[S_i] = true
    sj[S_j] = true

	dist := [][]int{{1, 0}, {-1, 0}, {0, -1}, {0, 1}} // 四个方向操作
	var cur Node
	var ni, nj int
	// 先从S开始遍历
	queue := []Node{{S_i, S_j}}
	for len(queue) > 0 {
		cur = queue[0]
		queue = queue[1:]

		for _, d := range dist {
			ni = cur.i + d[0]
			nj = cur.j + d[1]
			if ni < 0 || ni >= n || nj < 0 || nj >= m || k[ni][nj] == nS || k[ni][nj] == nH {
				continue
			}
			if k[ni][nj] == nE || isAdjoin(ni, E_i) || isAdjoin(nj, E_j) { // 与E相邻或同行或同列
				fmt.Println("YES")
				return
			}
			k[ni][nj] = nS
            si[ni],sj[nj] = true, true
			queue = append(queue, Node{ni, nj})
		}
	}

	// 再从E开始遍历
	queue = []Node{{E_i, E_j}}
	for len(queue) > 0 {
		cur = queue[0]
		queue = queue[1:]

		for _, d := range dist {
			ni = cur.i + d[0]
			nj = cur.j + d[1]
			if ni < 0 || ni >= n || nj < 0 || nj >= m || k[ni][nj] == nE || k[ni][nj] == nH {
				continue
			}
			if k[ni][nj] == nS || 
                si[ni] || (ni-1>=0&&si[ni-1]) || (ni+1<n&&si[ni+1]) || 
                sj[nj] || (nj-1>=0&&sj[nj-1]) || (nj+1<m&&sj[nj+1]) { // 与S可达的位置相邻或同行或同列
				fmt.Println("YES")
				return
			}
			k[ni][nj] = nE
			queue = append(queue, Node{ni, nj})
		}
	}

    // 都没法满足,无法可达
	fmt.Print("NO")
}

发表于 2025-09-07 17:42:42 回复(0)
用dfs过了,用一个函数,求出S或者E的可达行列边界,比如说
..###
S####
#####
##.E#
这个题S的可达row为0~1,可达col为0~1,E的可达row为4~4,E的可达col为2~3。而S可以发一发激光,我们就让他的row和col的边界都加一。比如S站在(0,1)向下发激光,那么S的col目前为1,就可以和任何E的col为2的可达点联通。因此S发射激光后的可达row为 -1~2,可达col为 -1~2,E的可达row为4~4,E的可达col为2~3。再看S和E的可达row,可达col的区间只要有交集就证明S和E可达。BFS的方法暂时还没想出来。
发表于 2025-09-03 07:17:48 回复(0)
测试用例46有问题
4 6
S.....
######
######
E#####
预期输出YES?
发表于 2025-08-21 20:59:30 回复(2)