【总结】双向BFS

双向BFS

若相遇点是起始点,则步数为 s t e p n o w ∗ 2 step_{now}*2 stepnow2 ,因为起点和终止点拓展的步数相同

若相遇点是终止点,则步数为 s t e p n o w ∗ 2 − 1 step_{now}*2-1 stepnow21,因为起点多拓展了一步

骑士精神

本题可以用双向BFS,因为 s t e p < = 15 step<=15 step<=15 ,所以总拓展数 <= 8 8 8^8 88

第一种方法是暴力枚举是否有相同的棋盘,可能超时。

第二种方法是将状态hash成一个整数。这个整数可以用 m a p map map 存。

笨笨的跳棋

定义四元组 ( x 1 , x 2 , x 3 , x 4 ) (x1,x2,x3,x4) (x1,x2,x3,x4)

考虑双向BFS,当跳到相同状态时就求到了最小步数。

考虑怎么判断是否超过步数。

注意到当取出 x x x 时,此时已走的步数为 n o w . w ∗ 2 + m p [ s ] now.w*2+mp[s] now.w2+mp[s] (当前方向已走步数+反方向已走最远步数),可以在此处剪枝。

也就是说,每个方向一次扩展一层,当一层在扩展时,另一层是不动的,当一边扩展完一层时,另一边才会扩展。

字串变换

显然用一个字符串来表示状态。

关键是怎么转移。

暴力即可。

Nightmare Ⅱ

这题用一个队列极难编写。。。

突然觉得 g m gm gm 的方法一点都不好。。。

双向BFS还是要用两个队列。。。分别模拟。。。。用一个 r e s res res 记录当前搜索到的层数。。。用二维 v i s vis vis 数组记录。。。

结构体里的东西越少越好。。。

这道题有点特殊,是bfs里套bfs。bfs外面走一层,里面只跑当前轮次。

最后一个问题:bfs里面的循环是不是一定走三步?其实这个问题可以用贪心解释。当然是走得越多越好,而bfs里面已经将走不满3步的情况算了。所以归根结底,本题还是只有一个bfs,只不过外层bfs是来限制次数的。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
int n, m, dp[810][810];
int res;
int dx[4] = { -1, 0, 1, 0 }, dy[4] = { 0, 1, 0, -1 };
bool vis[810][810][2];
char a[810][810];
struct Node {
    //(x,y),step,dir
    int x, y;
} st, ed, goal[5];
queue<Node> Q[2];
bool check(int x,int y) {
    //一个位置合法应满足:当前时间没有被鬼占领,没有障碍物,没有出界
    if (x < 1 || x > n || y < 1 || y > m || a[x][y] == 'X')
        return 0;
    //当前时间t,鬼的覆盖应为2(step+1)
    for (int i = 0; i < 2; i++)
        if (abs(x - goal[i].x) + abs(y - goal[i].y) <= 2 * res)
            return 0;
    return 1;
}
bool bfs(int ind) {
	int t=Q[ind].size(); //只跑当前轮次
	while(t--) {
		Node now=Q[ind].front(); Q[ind].pop();
		if(!check(now.x,now.y)) continue;
		for(int i=0;i<4;i++) {
			int x=now.x+dx[i],y=now.y+dy[i];
			if(!check(x,y)||vis[x][y][ind]) continue;
			if(vis[x][y][ind^1]) return 1;
			vis[x][y][ind]=1;
			Q[ind].push({x,y});
		}
	} 
	return 0;
}
int two_waybfs() {
    //可以停留,所以状态可以保留(已经保证了相遇点合法)
    while(Q[0].size()) Q[0].pop();
    while(Q[1].size()) Q[1].pop();
    Q[0].push(st),Q[1].push(ed);
    vis[st.x][st.y][0]=1;
    vis[ed.x][ed.y][1]=1;
    res=0;
    while (Q[0].size()&&Q[1].size()) {
    	res++;
    	int cou=3; while(cou--) if(bfs(0)) return res;
    	if(bfs(1)) return res;
    }
    return -1;
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
    	memset(vis,0,sizeof(vis));
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) scanf("%s", a[i] + 1);
        int num = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++) {
                if (a[i][j] == 'Z')
                    goal[num].x = i, goal[num].y = j,num++;
                if (a[i][j] == 'M')
                    st.x = i, st.y = j;
                if (a[i][j] == 'G')
                    ed.x = i, ed.y = j;
            }
//        for(int i=0;i<num;i++) printf("kkk %d %d\n",goal[i].x,goal[i].y);
        printf("%d\n", two_waybfs());
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值