走迷宫(可使用激光)

题目描述:
给定一个 n×m 的迷宫,迷宫由 "#" 与"." 两种字符组成。其中 "#" 代表障碍物,"." 表示空地。迷宫中还有一个起点 "S" 和一个终点 "E" ,它们都可以视为空地。
由于近期迷宫发生了塌方,导致起点和终点之间可能并不连通。幸运的是,你拥有一种超能力——在迷宫中移动时(移动方向为上、下、左、右四个方向之一),可以在当前位置朝任一方向(上、下、左、右四个方向之一)释放激光。激光能够清除该方向上所有的障碍物,并且这种超能力至多只能使用一次。
现在,你需要判断是否能利用这种超能力成功从起点到达终点。

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

输出描述:
能够到达终点输出 YES ;否则输出 NO

思路1:使用DFS暴力搜索,在搜索的过程中一但遇到障碍物就使用激光,然后接着搜索.

#include<bits/stdc++.h>
using namespace std;
int m,n;
char maz3[1010][1010];//这个是迷宫
bool visited[1010][1010];//判断走过的地方防止无限递归
int sx,sy,ex,ey;//起始点和终止点

//使用激光清除障碍物,为了方便撤回操作,使用感叹号代替障碍物
void usetrick(int x,int y,int dir){
	while(0 <= x and 0 <= y and x < m and y < n){
		if(maz3[x][y] == '#')  maz3[x][y] = '!';
		if(dir == 0) x--;
		if(dir == 1) y++;
		if(dir == 2) x++;
		if(dir == 3) y--;
	}
}

void unusetrick(int x,int y,int dir){
	while(0 <= x and 0 <= y and x < m and y < n){
		if(maz3[x][y] == '!')  maz3[x][y] = '#';
		if(dir == 0) x--;
		if(dir == 1) y++;
		if(dir == 2) x++;
		if(dir == 3) y--;
	}
}	

//DFS递归搜索
bool ch3ck(bool trick,int x,int y){
	if(x == ex and y == ey) return true;
	bool flag = false;//能不能走到E点
	
	for(int i=0;i<4;++i){//四个方向
		int nx = x,ny = y;//现在的坐标
		
		if(i == 0) nx--;
		if(i == 1) ny++;
		if(i == 2) nx++;
		if(i == 3) ny--;
		
		if(0 <= nx and 0 <= ny and nx < m and ny < n and !visited[nx][ny]){//没走过并且在迷宫内
			visited[nx][ny] = true;
			if(maz3[nx][ny] != '#'){//不是障碍物的话就往个下走
				if(ch3ck(trick,nx,ny)) flag = true;
			}
			else if(trick){//如果是障碍物就清除并且标记使用了trick,trick在之后的递归就不能用了
				trick = false;
				usetrick(x,y,i);
				if(ch3ck(trick,nx,ny)) flag = true;
				unusetrick(x,y,i);
				trick = true;//这两行是递归回溯
			}
			visited[nx][ny] = false;
		}
	}
	
	return flag;
}

int main(){
    
    cin >> m >> n;
    for(int i=0;i<m;++i){
        for(int j=0;j<n;++j){
        	char ch;
        	cin >> ch;
    		maz3[i][j] = ch;
        	if(ch == 'S'){
        		sx = i;
        		sy = j;
        	}
        	if(ch == 'E'){
        		ex = i;
        		ey = j;
        	}
        }
    }
    
    bool trick = true;
    if(ch3ck(trick,sx,sy)){
    	cout << "YES";
    }
    else{
    	cout << "NO";
    }
    
    
    return 0;
}

由于该算法的复杂度为O(n*n*m*m),显然超时了(10^12)


思路2:使用队列BFS代替递归DFS

#include<bits/stdc++.h>
using namespace std;
int m,n;
char maz3[1010][1010];
bool visited[1010][1010];
int sx,sy,ex,ey;

void usetrick(int x,int y,int dir){
	while(0 <= x and 0 <= y and x < m and y < n){
		if(maz3[x][y] == '#')  maz3[x][y] = '!';
		if(dir == 0) x--;
		if(dir == 1) y++;
		if(dir == 2) x++;
		if(dir == 3) y--;
	}
}

void unusetrick(int x,int y,int dir){
	while(0 <= x and 0 <= y and x < m and y < n){
		if(maz3[x][y] == '!')  maz3[x][y] = '#';
		if(dir == 0) x--;
		if(dir == 1) y++;
		if(dir == 2) x++;
		if(dir == 3) y--;
	}
}	

bool check(int x,int y){
	int dx[] = {-1,0,1,0};
	int dy[] = {0,1,0,-1};
	
	queue<pair<int,int>> q;
	q.push({x,y});
	visited[x][y] = true;
	
	while(!q.empty()){
		pair<int,int> pnow = q.front();q.pop();
		int x = pnow.first,y = pnow.second;
		
		if(x == ex and y == ey) return true;
		
		for(int i=0;i<4;++i){//四个方向
			int nx = x+dx[i],ny = y+dy[i];
			
			if(nx >= 0 and ny >= 0 and nx < m and ny < n){
				if(maz3[nx][ny] != '#' and !visited[nx][ny]){
					visited[nx][ny] = true;
					q.push({nx,ny});
				}
			}
		}
	}
	return false;
}

bool ch3ck(){
	int dx[] = {-1,0,1,0};
	int dy[] = {0,1,0,-1};
	
	queue<pair<int,int>> q;
	q.push({sx,sy});
	visited[sx][sy] = true;
	
	while(!q.empty()){
		pair<int,int> pnow = q.front();q.pop();
		int x = pnow.first,y = pnow.second;
		
		if(x == ex and y == ey) return true;
		
		for(int i=0;i<4;++i){//四个方向
			int nx = x+dx[i],ny = y+dy[i];
			
			if(nx >= 0 and ny >= 0 and nx < m and ny < n){
				if(maz3[nx][ny] != '#' and !visited[nx][ny]){
					visited[nx][ny] = true;
					q.push({nx,ny});
				}
				else{
					usetrick(nx,ny,i);
					bool visitedTmp[1010][1010];
					memcpy(visitedTmp,visited,sizeof(visited));
					if(check(nx,ny)) return true;
					memcpy(visited,visitedTmp,sizeof(visited));
					unusetrick(nx,ny,i);
				}
			}
		}
	}
	return false;
}

int main(){
    
    cin >> m >> n;
    for(int i=0;i<m;++i){
        for(int j=0;j<n;++j){
        	char ch;
        	cin >> ch;
    		maz3[i][j] = ch;
        	if(ch == 'S'){
        		sx = i;
        		sy = j;
        	}
        	if(ch == 'E'){
        		ex = i;
        		ey = j;
        	}
        }
    }
    
    if(ch3ck()){
    	cout << "YES";
    }
    else{
    	cout << "NO";
    }
    
    
    return 0;
}

在ch3ck函数中若使用激光,进入check函数搜索,虽然这两个函数都是O(m*n),但嵌套之后跟上一个代码复杂度是一样的.


思路3:从起点和重点分别做BFS搜索,找出从起点和重点能到达的地方,这一部分的复杂度是O(2*m*n) = O(m*n)
做完这一部分之后,存入两个二维数组里,方便后续工作.
例如:

很直接的能想到,如果在这个5*5迷宫里有横的一条线或者竖的一条线,这线上既有S能到的地方,又有E能到的地方,不就是能到达吗(使用激光).或者是更简单的情况不使用激光也能到达.
但是使用激光后开出来的路可能从两边走可能到达终点!!
所以解法是在比较时同时比较S能到达的一条线和E能到达的三条线

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
int m,n,sx,sy,ex,ey;
int maz3[1010][1010][3];

void chSck(){
	int dx[]={-1,0,1,0};
	int dy[]={0,1,0,-1};
    queue<pair<int,int>> q;
    q.push({sx,sy});
    maz3[sx][sy][1] = 1;
    while(!q.empty()){
    	int x = q.front().first,y = q.front().second;q.pop();
    	
    	for(int i=0;i<4;++i){
    		int nx = x+dx[i],ny = y+dy[i];
    		if(nx >= 0 and ny >= 0 and nx < m and ny < n){
    			if(maz3[nx][ny][0] != '#' and !maz3[nx][ny][1]){
    				maz3[nx][ny][1] = 1;
    				q.push({nx,ny});
    			}
    		}
    	}
    }
}

void chEck(){
	int dx[]={-1,0,1,0};
	int dy[]={0,1,0,-1};
    queue<pair<int,int>> q;
    q.push({ex,ey});
    maz3[ex][ey][2] = 1;
    while(!q.empty()){
    	int x = q.front().first,y = q.front().second;q.pop();
    	
    	for(int i=0;i<4;++i){
    		int nx = x+dx[i],ny = y+dy[i];
    		if(nx >= 0 and ny >= 0 and nx < m and ny < n){
    			if(maz3[nx][ny][0] != '#' and !maz3[nx][ny][2]){
    				maz3[nx][ny][2] = 1;
    				q.push({nx,ny});
    			}
    		}
    	}
    }
}

bool ch3ck(){
	
    for(int i=0;i<m;++i){
    	int ToS = 0;
    	int ToE = 0;
    	for(int j=0;j<n;++j){
    		ToS += maz3[i][j][1];
    		ToE += maz3[i][j][2];
    		if(i>0)ToE += maz3[i-1][j][2];
    		if(i<m-1)ToE += maz3[i+1][j][2];
    	}
    	if(ToS and ToE) return true;
    }
    for(int j=0;j<n;++j){
    	int ToS = 0;
    	int ToE = 0;
    	for(int i=0;i<m;++i){
    		ToS += maz3[i][j][1];
    		ToE += maz3[i][j][2];
    		if(j>0)ToE += maz3[i][j-1][2];
    		if(j<n-1)ToE += maz3[i][j+1][2];
    	}
    	if(ToS and ToE) return true;
    }
    return false;
}

int main(){
    cin >> m >> n;
    
    for(int i=0;i<m;++i){
    	for(int j=0;j<n;++j){
    		char ch;
    		cin >> ch;
    		maz3[i][j][0] = ch;
    		if(ch == 'S'){
    			sx = i;
    			sy = j;
    		}
    		if(ch == 'E'){
    			ex = i;
    			ey = j;
    		}
    	}
    }
    chEck();
    chSck();
    
    // for(int i=0;i<m;++i){
    	// for(int j=0;j<n;++j){
    		// cout << maz3[i][j][1] << ' ';
    	// }
    	// cout << endl;
    // }
    // cout << endl;
    // for(int i=0;i<m;++i){
    	// for(int j=0;j<n;++j){
    		// cout << maz3[i][j][2] << ' ';
    	// }
    	// cout << endl;
    // }
    
    if(ch3ck()) cout << "YES";
    else cout << "NO";
    
    
    return 0;
}
posted @ 2024-11-16 15:56  shen_kong  阅读(6)  评论(0编辑  收藏  举报