???牛客周赛55:虫洞操纵者

题目描述

          \,\,\,\,\,\,\,\,\,\,你需要在一个可以上下左右移动的 n×nn\times nn×n 棋盘上解开一个迷宫:棋盘四周都是墙;每个方格要么是可以通过的空方格 ′0′\sf '0'′0′ ,要么是不可通过的墙方格 ′1′\sf '1'′1′ ;你可以沿着空方格通行;你已经被传送到了迷宫的左上角(第 111 行第 111 列的位置),你知道终点位于迷宫右下角(第 nnn 行第 nnn 列的位置)。
          \,\,\,\,\,\,\,\,\,\,别人都不知道的是,你是一个虫洞大师,当你所在的位置能同时看到左右两侧或上下两侧最近的那两面相对的墙时,你可以在这两面墙上开启虫洞,靠近后从一侧进入,穿越它到达另一侧。
          \,\,\,\,\,\,\,\,\,\,现在,你准备好以最短的步数离开迷宫了吗!

输入描述:

 

          \,\,\,\,\,\,\,\,\,\,第一行输入一个整数 n(2≤n≤103)n\left(2\le n \le 10^3\right)n(2≤n≤103) 代表迷宫的大小。

          \,\,\,\,\,\,\,\,\,\,此后 nnn 行,第 iii 行输入 nnn 个整数 ai,1,ai,2,…,ai,n(0≤ai,j≤1)a_{i,1},a_{i,2},\dots,a_{i,n} \left(0\le a_{i,j}\le 1\right)ai,1​,ai,2​,…,ai,n​(0≤ai,j​≤1) 代表迷宫中第 iii 行第 jjj 列这个格子中的情况,其中 000 代表空方格,111 代表墙方格。保证起点和终点不为墙壁。

输出描述:

          \,\,\,\,\,\,\,\,\,\,在一行上输出一个整数,代表离开迷宫需要的最短步数,如果无论如何都无法离开迷宫,则直接输出 −1-1−1 。

示例1

输入

复制5 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0

5
0 0 0 1 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 0
1 0 0 0 0

输出

复制3

3

说明

 

          \,\,\,\,\,\,\,\,\,\,该样例示意图如下图所示。全部墙壁的位置都使用粗黑线标注。先从 (1,1)(1,1)(1,1) 向右移动一步走到 (1,2)(1,2)(1,2) ,此时可以开启一个虫洞(使用紫色箭头标注),向上移动一步到达 (5,2)(5,2)(5,2) ;此时可以开启第二个虫洞(使用红色箭头标注),向左移动一步到达 (5,5)(5,5)(5,5) 。

          \,\,\,\,\,\,\,\,\,\,

          \,\,\,\,\,\,\,\,\,\,注意,在该样例中,不能直接从 (1,1)(1,1)(1,1) 开启虫洞(使用黑色箭头标注)到达 (1,5)(1,5)(1,5) ,因为 (1,4)(1,4)(1,4) 存在两面墙遮挡了视线;不能直接从 (4,3)(4,3)(4,3) 开启虫洞(使用绿色箭头标注)到达 (1,3)(1,3)(1,3) ,因为这两面墙不是相对的。

          \,\,\,\,\,\,\,\,\,\,

示例2

输入

复制5 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0

5
0 0 0 1 0
1 1 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 0

输出

复制5

5

说明

          \,\,\,\,\,\,\,\,\,\,

示例3

输入

复制2 0 1 1 0

2
0 1
1 0

输出

复制-1

-1

做法

#include<bits/stdc++.h>
using namespace std;

int n;
int a[1010][1010];
int vis[1010][1010];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};

struct ty{
    int x,y;
    int dis;
};

int sign;
queue<ty> q;

void bfs(){
	
    q.push({1,1,0});

    while(!q.empty()){
    	
        ty tmp=q.front();
        q.pop();
        
        if(tmp.x==n&&tmp.y==n){
            cout<<tmp.dis;
            sign=1;
            return ;
        }
        
        if(vis[tmp.x][tmp.y]) continue;
        vis[tmp.x][tmp.y]=1;
        
        for(int i=0;i<4;i++){
        	
            int xx=dx[i]+tmp.x;
            int yy=tmp.y+dy[i];
            
            if(a[xx][yy]==0){//墙壁

                //反方向搜,直到碰到墙壁
            	while(a[xx-dx[i]][yy-dy[i]]) xx=xx-dx[i],yy=yy-dy[i];

			}
        
            if(vis[xx][yy]) continue;
			q.push({xx,yy,tmp.dis+1});
	
            
        }
    }
}

int main(){
	
    scanf("%d",&n);
    
    for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
			a[i][j]^=1;//异或一下的话路变成1,墙壁变成0,四周的墙壁也是0
		}
    }
    
    bfs();
    if(sign==0) cout<<-1;
    
    
}

我的做法错了,不知道为啥,和题解的做法不一样的就是我四周的墙壁单独处理了。然后穿墙的时候找穿越的位置也不是一个个暴力找,而是用了二分函数找。但是只过了20%,实在是找不到为什么。

#include<bits/stdc++.h>
using namespace std;

int n;
int a[1010][1010];
vector<int> h[1010],l[1010];
int vis[1010][1010];
int vis1[1010][1010][4];
int dx[]={0,0,1,-1};//右左下上
int dy[]={1,-1,0,0};

struct ty{
    int x,y;
    int dis;
};

int sign;
queue<ty> q;

void bfs(){

    q.push({1,1,0});

    while(!q.empty()){

        ty tmp=q.front();
        q.pop();
        
        vis[tmp.x][tmp.y]=1;
        
        if(tmp.x==n&&tmp.y==n){
            cout<<tmp.dis;
            sign=1;
            return ;
        }
        
        for(int i=0;i<4;i++){
        	
            int xx=dx[i]+tmp.x;
            int yy=tmp.y+dy[i];
            
            //四周墙壁
            if(yy<1){//左
                if(h[xx].size()==0&&!vis[xx][n]) q.push({xx,n,tmp.dis+1});
                else if(h[xx].size()!=0&&!vis[xx][h[xx][0]-1])q.push({xx,h[xx][0]-1,tmp.dis+1});
                continue;
            }
            
            if(xx<1){//上
                if(l[yy].size()==0&&!vis[n][yy]) q.push({n,yy,tmp.dis+1});
                else if(l[yy].size()!=0&&!vis[l[yy][0]-1][yy]) q.push({l[yy][0]-1,yy,tmp.dis+1});
                continue;
            }
            
            if(yy>n){//右
                if(h[xx].size()==0&&!vis[xx][1]) q.push({xx,1,tmp.dis+1});
                else if(h[xx].size()!=0&&!vis[xx][h[xx][h[xx].size()-1]+1]) q.push({xx,h[xx][h[xx].size()-1]+1,tmp.dis+1});
                continue;
            }
            
            if(xx>n){//下
                if(l[yy].size()==0&&!vis[1][yy]) q.push({1,yy,tmp.dis+1});
                else if(l[yy].size()!=0&&!vis[l[yy][l[yy].size()-1]+1][yy]) q.push({l[yy][l[yy].size()-1]+1,yy,tmp.dis+1});
                continue;
            }
            
            
            if(a[xx][yy]==1){//墙壁
            
            	if(vis1[xx][yy][i]) continue;
            	vis1[xx][yy][i]=1;
            		
                if(i==0) {//右

                    if(h[xx].size()==1) q.push({xx,1,tmp.dis+1});
                    
                    else{
                    	int id=lower_bound(h[xx].begin(),h[xx].end(),yy)-h[xx].begin()-1;
                    	if(id!=-1) q.push({xx,h[xx][id]+1,tmp.dis+1});
                    	else q.push({xx,1,tmp.dis+1});
					}
                        
                }
                
                if(i==1){//左
                    
                    if(h[xx].size()==1) q.push({xx,n,tmp.dis+1});
                    
                    else{
                    	int id=upper_bound(h[xx].begin(),h[xx].end(),yy)-h[xx].begin();
                    	if(id!=h[xx].size()) q.push({xx,h[xx][id]-1,tmp.dis+1});
                    	else q.push({xx,n,tmp.dis+1});
					}
					
                }
                
                if(i==2){//下
                    
                    if(l[yy].size()==1) q.push({1,yy,tmp.dis+1});
                    
                    else{
                    	int id=lower_bound(l[yy].begin(),l[yy].end(),xx)-l[yy].begin()-1;
                    	if(id!=-1) q.push({l[yy][id]+1,yy,tmp.dis+1});
                    	else q.push({1,yy,tmp.dis+1});
					}
                    
                }
                
                if(i==3){//上
                    
                    if(l[yy].size()==1) q.push({n,yy,tmp.dis+1});
                    
                    else{
                    	int id=upper_bound(l[yy].begin(),l[yy].end(),yy)-l[yy].begin();
                    	if(id!=l[yy].size()) q.push({l[yy][id]-1,yy,tmp.dis+1});
                    	else q.push({n,yy,tmp.dis+1});
					}
                    
                }
                
                
            }
                 
            else {//正常的路
            	if(vis[xx][yy]) continue;
				q.push({xx,yy,tmp.dis+1});
			}
            
        }
    }
}

int main(){
	
    scanf("%d",&n);
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
            if(a[i][j]) {
                h[i].push_back(j);
                l[j].push_back(i);
			} 
		}
    }
    
    for(int i=1;i<=n;i++){
        sort(l[i].begin(),l[i].end());
        sort(h[i].begin(),h[i].end());
    }
    
    bfs();
    if(sign==0) cout<<-1;
    
    
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值