噩梦

本文介绍了如何使用双指针策略和动态规划优化解决一道涉及鬼畜路径问题的算法题。作者通过两种方法探讨了如何避免DFS或BFS导致的TLE,并分享了一个新的技巧,即通过男生和女生分别使用队列进行搜索,每次拓展3步,有效防止了重复路径的问题,从而提高了算法效率。文章提供了详细的伪代码和实际代码示例,展示了如何在不使用标记的情况下实现正确解答。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.前言

鬼畜的题意,真的成了我的噩梦

2.题解

这道题中,小erriyueerriyueerriyue的3步是可以漂移转弯的,我因为这个差点顺网线去打爆出题人的头。

(1).法1

首先我尝试了一下我们之前的写法,用一个flagflagflag标记正反,可是我很快发现:男生的拓展非常麻烦,因为只满足曼哈顿距离小于333也不对,因为路径上可能会出现不可路过的点,所以必须老老实实的用dfsdfsdfsbfsbfsbfs跑,但是防止跑重复的点很麻烦,因为重置标记非常耗时间,妥妥的TLETLETLE,但如果不重置的话会WAWAWA,因为走到这个点的步数不同的路径花费不一样,经过我的慎重考虑,我决定——————不标记,直接暴力跑,可以很高兴的宣布结果,TLETLETLE,所以这个方法代码实现十分麻烦

(2).法2(新get到的一个不错的技巧)

男生一个队列,女生一个队列。女生的更新照旧,男生的更新333步一组,但是不能这样写

//伪代码
for (int i = 1; i <= 3; i++) {
	while (q_boy.size ()) {
		//拓展
	}
}

因为这样写的话把已经拓展的东西也加入了队列,就不知道拓展了几次

代码十分好写,细节见代码

参考代码

#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;

template <typename T> int read (T &x) {x = 0; T f = 1;char tem = getchar ();while (tem < '0' || tem > '9') {if (tem == '-') f = -1;tem = getchar ();}while (tem >= '0' && tem <= '9') {x = (x << 1) + (x << 3) + tem - '0';tem = getchar ();}x *= f; return 1;}
template <typename T> void write (T x) {if (x < 0) {x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }

const int Maxn = 800;

int Times, n, m;
int ans[2][Maxn + 5][Maxn + 5];//ans[1][i][j]记录男生走到 (i, j)的最少步数,ans[2][i][j]同理 
char c[Maxn + 5][Maxn + 5];//地图 

int tox[10] = {0, 1, -1, 0, 0};
int toy[10] = {0, 0, 0, 1, -1};

int tot;
struct node {
	int step, x, y, Up;
	bool flag;
}s, t, z[4];

queue <node> qb, qg;

int Dis (node x, node y) {//曼哈顿距离 
	return Abs (x.x - y.x) + Abs (x.y - y.y);
}

int Two_Way_Bfs () {
	while (qb.size ()) qb.pop ();//男生队列清空 
	while (qg.size ()) qg.pop ();//女生队列清空 
	
	qb.push (s); qg.push (t);
	memset (ans, -1, sizeof ans);
	ans[s.flag][s.x][s.y] = ans[t.flag][t.x][t.y] = 0;//初始化 
	
	while (qb.size () || qg.size ()) {
		int step;
		if (qb.size ())//如果男生还能走 
			step = qb.front ().step;
		for (int i = 1; i <= 3; i++) {//拓展三次(关键代码) 
			for (int j = qb.size (); j; j--) {//固定取出次数 qb.size (),保证同一个i下取出的步数一样 (巧妙点) 
				node Now = qb.front (), Next; qb.pop ();
				if (Dis (Now, z[1]) <= 2 * step + 2 || Dis (Now, z[2]) <= 2 * step + 2) continue;//被鬼抓到 
				Next = Now; Next.step = step + 1;
				for (int k = 1; k <= 4; k++) {
					int x = Now.x + tox[k], y = Now.y + toy[k];
					Next.x = x; Next.y = y;
					if (x < 1 || x > n) continue;
					if (y < 1 || y > m) continue;
					if (c[x][y] == 'X') continue;
					if (ans[Next.flag][x][y] != -1) continue;
					if (Dis (Next, z[1]) <= 2 * Next.step || Dis (Next, z[2]) <= 2 * Next.step) continue;
					
					if (ans[Next.flag ^ 1][Next.x][Next.y] != -1) //如果女生能走到这里 
						return Max (Next.step, ans[Next.flag ^ 1][Next.x][Next.y]);
					ans[Now.flag][x][y] = Next.step;//记录路径 
					qb.push (Next);
					
//					printf ("x = %d, y = %d, flag = %d, step = %d, Up = %d\n", Next.x, Next.y, Next.flag, Next.step, Next.Up);
				}
			}
		}
		
		//女生的同理,作者咕掉 
		if (qg.size ())
			step = qg.front ().step;
		for (int i = 1; i <= 1; i++) {
			for (int j = qg.size (); j; j--) {
				node Now = qg.front (), Next; qg.pop ();
				if (Dis (Now, z[1]) <= 2 * step + 2 || Dis (Now, z[2]) <= 2 * step + 2) continue;
				Next = Now; Next.step = step + 1;
				for (int k = 1; k <= 4; k++) {
					int x = Now.x + tox[k], y = Now.y + toy[k];
					Next.x = x; Next.y = y;
					if (x < 1 || x > n) continue;
					if (y < 1 || y > m) continue;
					if (c[x][y] == 'X') continue;
					if (ans[Next.flag][x][y] != -1) continue;
					if (Dis (Next, z[1]) <= 2 * Next.step || Dis (Next, z[2]) <= 2 * Next.step) continue;
					
					if (ans[Next.flag ^ 1][Next.x][Next.y] != -1) 
						return Max (Next.step, ans[Next.flag ^ 1][Next.x][Next.y]);
					ans[Now.flag][x][y] = Next.step;
					qg.push (Next);
					
//					printf ("x = %d, y = %d, flag = %d, step = %d, Up = %d\n", Next.x, Next.y, Next.flag, Next.step, Next.Up);
				}
			}
		}
	}
	return -1;
}

int main () {
	scanf ("%d", &Times);
	while (Times--) {
		tot = 0;
		
		scanf ("%d %d", &n, &m);
		for (int i = 1; i <= n; i++) {
			scanf ("%s", c[i] + 1);
			for (int j = 1; j <= m; j++) {
				if (c[i][j] == 'M') {//找到男生的位置 
					s.step = 0;
					s.x = i;
					s.y = j;
					s.flag = 1;
					s.Up = 3;
				}
				if (c[i][j] == 'G') {//找到女生的位置 
					t.step = 0;
					t.x = i;
					t.y = j;
					t.flag = 0;
					t.Up = 1;
				}
				if (c[i][j] == 'Z') {//找到鬼的位置 
					z[++tot].x = i;
					z[tot].y = j;
				}
			}
		}
		
		cout << Two_Way_Bfs () << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值