经典拐弯问题(dfs || bfs)

题目
这是搜索题中的经典拐弯问题,难点在于怎么标记已经搜索过的点。

对于某一个点来说,从不同路径但是最后从同一个方向到达这个点所需的拐弯数可能是不同的。

例如:
5 5
S.*T.   
.....
.....
.....
.....

同样向右走到 (1,2)的两种走法,一种是从右往下,然后再往右,拐了两次弯;而另一种先向下,然后再往右,
实际上只用拐一次弯,所以在搜索过程中需要更新更优解而另一种先向下,然后再往右,实际上只用拐一次弯。
所以在搜索过程中需要更新更优解。

而我们想要求的是是否可以在拐弯尽量少的情况下在两次拐弯内到达终点。

所以如果从一个方向到了一个点,我们需要比较当前的拐弯次数和从这个方向到这个点的最小拐弯次数的大小,如果当前的拐弯次数更少(也就是说当前路径方案是一个更优解的话),就将这个之前已经走过的点的此方向的最小拐弯次数更新,并且将这个点的拐弯次数变成更小的拐弯次数,并且放入队列更新。

然后不断的更新更优路径,如果当前的拐弯次数已经大于2了,就换另外一个点更新,如果能够在拐弯次数在2之内到达终点,就打标记。

这道题判断是否拐弯了的方法也很有意思,就是直接判断当前要走的方向和之前走到这个点的方向是否相同,相同的话就是没拐弯,否则就是拐弯了,如果是初始点的话就特判一下。

代码:
(目前只写明白了bfs的代码,dfs的待补):

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define endl '\n'
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;

const int N = 1e3 + 5;
int n, m, f;

char a[N][N];
int vis[N][N][5];

struct node
{
	int x, y;
	int r, fx;
};

int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};

void bfs(int x, int y)
{
	queue<node> Q;
	node head;
	head.x = x, head.y = y, head.fx = -1, head.r = 0;
	Q.push(head);
	while(Q.size() >= 1)
	{
		node now = Q.front();
		Q.pop();
		for(int i = 0; i < 4; i ++)
		{
			int tem;  //下一步的拐弯次数
			if(now.fx == -1) tem = 0;   //初始方向
			else if(now.fx == i) tem = now.r;   //和之前的方向相同
			else if(now.fx != i) tem = now.r + 1;   //和之前的方向不同
			int xx = now.x + dx[i], yy = now.y + dy[i];
			if(xx < 0 || yy < 0 || xx >= n || yy >= m || tem > 2) continue;  //可行性剪枝
			if(a[xx][yy] =='T')    //如果没超过两次拐弯就到达了终点的话,就做标记。
			{
				f = 1;
				return ;
			}
			if(a[xx][yy] == '*') continue;   //如果是墙的话就直接跳过。
			if(tem >= vis[xx][yy][i]) continue;   //如果之前此方向上的拐弯次数不大于此时,就跳过
			vis[xx][yy][i] = tem;     //如果此时是更优解的话,更新此方向上的vis数组
			node next;
			next.fx = i, next.r = tem, next.x = xx, next.y = yy;
			Q.push(next);
		}
	}
	return;
}

int main()
{
	int kx, ky;
	scanf("%d%d", &n, &m);
	getchar();
	for(int i = 0; i < n; i ++)
		gets(a[i]);
	for(int i = 0; i < n; i ++)
		for(int j = 0; j < m; j ++)
			if(a[i][j] == 'S') kx = i, ky = j;
	memset(vis,0x3f,sizeof(vis));
	bfs(kx, ky);
	if(f)
		printf("YES\n");
	else
		printf("NO\n");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值