双向搜索总结

双向搜索

一、双向搜索

当给出了起点状态与终点状态时,使用普通的搜索从起点向下搜索,则效率会很低,搜索树会非常庞大;

所以,可以使用双向搜索,及从起点与终点同时向中间搜索,搜索到同一个状态时,将从起点与终点搜索的值相加得到最终值的搜索;

一般给出 “始态” 与 “终态” 时,可以考虑使用双向搜索;

二、双向DFS

第一个 D F S DFS DFS 时,先搜索前一半的空间,存储所有可达的值;

第二个 D F S DFS DFS 时,再搜索后一半的空间,然后查询是否在前一半空间中出现过;

三、双向BFS

双向 B F S BFS BFS 维护两个对列, q 1 q1 q1 q 2 q2 q2 q 1 q1 q1 维护从起点开始搜索的状态, q 2 q2 q2 维护从终点开始搜索的状态,若在扩展状态时发现某个状态同时被 q 1 q1 q1 q 2 q2 q2 扩展到,此时找到的即为答案;

将开始状态放入q1;
将结束状态放入q2;
将开始状态标记为1;
将结束状态标记为2;
while (q1不为空 && q2不为空) {
    if (q1大小 > q2大小) {
        取队列 q2 队首元素;
    } else {
        取队列 q1 队首元素;
    }
    循环遍历取出元素的可扩展状态 {
        if (从q1取出) {
    		标记为1,放入q1队列;
    	} else {
			标记为2,放入q2队列;
        }
        if (该状态同时被标记为1与2) {
            找到答案,搜索结束;
        }
    }
}

也可以只使用一个对列,即在队列中再开一个变量标记从何处开始搜索的即可;

四、例题

导航系统

题目

小Q来到了一个随机的国度。这个国度由n座城市和m条双向道路构成。因为这个国度崇尚随机,因此m条边是用随机选择两端点的方式生成的。充满好奇的小Q想在这里进行k次随机的旅行,每次的起点和终点也是随机选择的。在每次出发之前,他会使用导航系统计算两点间最少需要经过几条道路。请写一个程序,帮助小Q计算两点间的最短路

输入

第一行包含3个正整数n,m,k(2<=n<=100000,1<=m<=300000,1<=k<=10000),分别表示点数、边数和询问数。

接下来m行,每行两个正整数u_i,v_i(1<=u_i,v_i<=n),表示一条双向道路。输入数据保证不会有重边和自环。

接下来k行,每行两个正整数u_i,v_i(1<=u_i,v_i<=n),表示一次询问。

输入数据保证随机生成,且除了样例之外均满足n=100000,m=300000。

本题共3组数据。

输出

输出k行,每行一个整数,即最少经过的边数,若无解输出-1。

思路

此题给出一个起点与一个终点,所以考虑双向搜索,状态扩展时,扩展每个点相连的点;

使用邻接表记录图,在询问时,使用并查集判断两点是否连通;

代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 300005
using namespace std;
int n, m, k, father[MAXN];
bool flag1[MAXN], flag2[MAXN];
vector < int > g[MAXN];
struct node {
	int x, z, f;
};
void firstset(int n) {
	for (int i = 1; i <= n; i++) {
		father[i] = i;
	}
	return;
}
int findset(int x) {
	if (x == father[x]) return x;
	return father[x] = findset(father[x]);
}
void push(int x, int y) {
	father[findset(x)] = findset(y);
	return;
}
int bfs(int s, int e) {
	queue < node > q;
	memset(flag1, false, sizeof(flag1));
	memset(flag2, false, sizeof(flag2));
	flag1[s] = true, flag2[e] = true;
	q.push( node ( { s, 0, 1 } ) );
	q.push( node ( { e, 0, 2 } ) );
	while (!q.empty()) {
		node t = q.front();
		q.pop();
		for (int i = 0; i < g[t.x].size(); i++) {
			if (t.f == 1) {
				flag1[g[t.x][i]] = true;
				q.push( node ( { g[t.x][i], t.z + 1, 1 } ) );
			} else {
				flag2[g[t.x][i]] = true;
				q.push( node ( { g[t.x][i], t.z + 1, 2 } ) );
			}
			if (flag1[g[t.x][i]] == true && flag2[g[t.x][i]] == true) {
				int ans = t.z + 1;
				if (t.f == 2) {
					while (!q.empty()) {
						node z = q.front();
						if (z.x == g[t.x][i] && z.f == 1) {
							ans += z.z;
							break;
						}
						q.pop();
					}
				} else {
					while (!q.empty()) {
						node z = q.front();
						if (z.x == g[t.x][i] && z.f == 2) {
							ans += z.z;
							break;
						}
						q.pop();
					}
				}
				return ans;
			}
		}
	}
}
int main() {
	scanf("%d %d %d", &n, &m, &k);
	firstset(n);
	for (int i = 1; i <= m; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		g[x].push_back(y);
		g[y].push_back(x);
		push(x, y);
	}
	for (int i = 1; i <= n; i++) {
		sort(g[i].begin(), g[i].end());
	}
	for (int i = 1; i <= k; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		if (x == y) printf("0\n");
		else if (findset(x) != findset(y)) printf("-1\n");
		else printf("%d\n", bfs(x, y));
	}
	return 0;
} 
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值