刷题记录:牛客NC208246胖胖的牛牛

传送门:牛客

题目描述:

每逢佳节胖三斤,牛牛在过去的节日里长胖了,连拐弯都困难,甚至会卡在门上,所以他很讨厌拐弯。给你一个
N*N(2≤N≤100)的方格中,‘x’表示障碍,‘.’表示没有障碍(可以走),牛牛可以从一个格子走到他相邻的四个
格子,但是不能走出这些格子。问牛牛从A点到B点最少需要转90度的弯几次。
输入:
3
. x A
. . .
B x .
输出:
2

一道BFS题,思路还是挺好想的,但是有一些细节需要注意

主要思路:

  1. 首先看完题目一般都会有一个大致思路了,不就是一道BFS的跑图题吗.直接将到每一个点的横纵坐标以及转弯的次数和方向存入我们的队列之中就行.然后每一次加入新点之时判断一下方向的关系再继续存点,当然我们需要使用优先队列进行优化,保证我们第一个到达的点是最优的,这样可以节省许多时间
  2. 但是对于我们的vis数组需要十分的注意,当时我就因为vis数组没有注意好一直卡在90分,搞了好久找到了一个hack样例,然后才发现vis数组出现了问题.

这道题的vis数组有两种写法,一种是

vis[x][y]=1;
for(int i=0;i<4;i++) {
	int nx=x+dx[i],ny=y+dy[i];
	if(vis[x][y]) continue;
	...
	...
}

还有一种是

if(vis[x][y][1-f.prex][1-f.prey]) continue;//用1减是为了防止出现负下标
vis[x][y][1-f.prex][1-f.prey]=1;
for(int i=0;i<4;i++) {
	int nx=x+dx[i],ny=y+dy[i];
	...
	...
}

对于第二种必须要加上方向!!为什么会有这种差别呢.

因为对于第一种方式来说,他在for循环外并没有continue操作,也就是他会枚举过去所有在队列中该点的情况,注意,因为存在队列中的点可能此时转弯次数是一样的,但是此时在该点的方向是不一样的!这样的话第一种方式是没有问题的,因为他是保证后序转弯次数在该点大于此时时不用进队列,但是此时会枚举所有已经在队列中的情况.但是对于第二种来说,我们保证的是当前情况是最优的,然而此时的情况是包括了我们的方向的.所以我们需要为此加入方向的存储.不然会导致后序方向不同的点不会被枚举到.

总结一下利弊,对于第一种方法来说,它虽然节省了数组的空间,但是会导致同一个点方向一样转弯次数一样的点也会被枚举,会导致队列中的空间开支和时间开支增大


下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
char mp[200][200];
int n;int st_x,st_y;
struct Node{
	int x,y,prex,prey,step;
	bool operator<(const Node &rhs) const {
		return step>rhs.step;
	}
};
int vis[200][200][5][5];
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
int flag=0;
void BFS(int sx,int sy) {
	priority_queue<Node>q;
	q.push({sx,sy,0,0,0});
	while(!q.empty()) {
		Node f=q.top();q.pop();
		int x=f.x,y=f.y;
		if(mp[x][y]=='B') {
			cout<<f.step<<endl;
			flag=1;
			exit(0);
		}
		if(vis[x][y][1-f.prex][1-f.prey]) continue;
		vis[x][y][1-f.prex][1-f.prey]=1;
		for(int i=0;i<4;i++) {
			int nx=x+dx[i],ny=y+dy[i];
			if(nx<1||ny<1||nx>n||ny>n||mp[nx][ny]=='x') continue; 
			if(f.prex==0&&f.prey==0) {
				q.push({nx,ny,dx[i],dy[i],f.step});
			}else {
				if(f.prex!=dx[i]&&f.prey!=dy[i]) {
					q.push({nx,ny,dx[i],dy[i],f.step+1});
				}else {
					q.push({nx,ny,dx[i],dy[i],f.step});
				}
			}
		}
	}
}
int main() {
	n=read();
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			cin>>mp[i][j];
			if(mp[i][j]=='A') {
				st_x=i;st_y=j;
			}
		}
	}
	BFS(st_x,st_y);
	if(flag==0) {
		printf("-1");
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值