双向广搜

所谓双向广搜,就是初始结点向目标结点和目标结点向初始结点同时扩展,直至在两个扩展方向上出现同一个结点,搜索结束。它适用的问题是,扩展结点较多,而目标结点又处在深沉,如果采用单纯的广搜解题,搜索量巨大,搜索速度慢是可想而知的,同时往往也会出现内存空间不够用的情况,这时双向广搜的作用就体现出来了。双向广搜对单纯的广搜进行了改良或改造,加入了一定的“智能因数”,使搜索能尽快接近目标结点,减少了在空间和时间上的复杂度。

通常有两种实现方法:

1、用一个队列来储存子状态,起点和终点先后入队,正向搜索和逆向搜索交替进行,两个方向的搜索交替扩展子状态。直到两个方向的搜索产生相同的子状态结束。

2、两个方向的搜索虽然是交替扩展子状态的。但是两个方向生成的子状态的速度不一定平衡。所以,可以每次选择子状态数较少的那个方向先进行扩展。这样就不会出现两个方向生成子状态的速度的不平衡,可以明显的提高效率哦。

例题

已知有两个字串 A, B 及一组字串变换的规则(至多 6 个规则):

A1→B1
A2→B2
… 规则的含义为:在 A 中的子串 A1 可以变换为 B1、A2 可以变换为 B2…。 例如:A=abcd B=xyz 变换规则为: abc → xu ud → y y → yz 则此时,A 可以经过一系列的变换变为 B,其变换的过程为: abcd → xud → xy → xyz 共进行了三次变换,使得 A 变换为 B。 输入格式
输入格式如下: A B
A1 B1
A2 B2
… … 第一行是两个给定的字符串 A 和 B。 接下来若干行,每行描述一组字串变换的规则。 所有字符串长度的上限为 20。 输出格式
若在 10 步(包含 10 步)以内能将 A 变换为 B ,则输出最少的变换步数;否则输出 NO ANSWER!。 输入样例:
abcd xyz
abc xu
ud y
y yz
输出样例:
3 #include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map> using namespace std; const int N = 6; int n;
string A, B;
string a[N], b[N]; int extend(queue<string>& q, unordered_map<string, int>&da, unordered_map<string, int>& db,
string a[N], string b[N])
{
int d = da[q.front()];
while (q.size() && da[q.front()] == d)
{
auto t = q.front();
q.pop(); for (int i = 0; i < n; i ++ )
for (int j = 0; j < t.size(); j ++ )
if (t.substr(j, a[i].size()) == a[i])
{
string r = t.substr(0, j) + b[i] + t.substr(j + a[i].size());
if (db.count(r)) return da[t] + db[r] + 1;
if (da.count(r)) continue;
da[r] = da[t] + 1;
q.push(r);
}
} return 11;
} int bfs()
{
if (A == B) return 0;
queue<string> qa, qb;
unordered_map<string, int> da, db; qa.push(A), qb.push(B);
da[A] = db[B] = 0; int step = 0;
while (qa.size() && qb.size())
{
int t;
if (qa.size() < qb.size()) t = extend(qa, da, db, a, b);
else t = extend(qb, db, da, b, a); if (t <= 10) return t;
if ( ++ step == 10) return -1;
} return -1;
} int main()
{
cin >> A >> B;
while (cin >> a[n] >> b[n]) n ++ ; int t = bfs();
if (t == -1) puts("NO ANSWER!");
else cout << t << endl; return 0;
}

A——Star算法

A星算法其实并不是最短路径算法,它找到的路径并不是最短的,它的目标首先是能以最快的速度找到通往目的地的路

A星算法也有很多弊端,就比如如果目的地不能到达 他还是会遍历寻路(可以寻路的时候判断 也可以算的上优化)

其次,如果地图过大,计算起来会很消耗时间,所以可以在计算上进行优化

下面说一下优化的一些可行的方法

一.距离过长时

距离很大,中间有很多障碍物时,A星的算法就会遇到瓶颈,不断加入的可行走点使得排序速度越来越慢,最后可能造成CPU阻塞无法动弹。很影响用户的体验,所以我们可以设置一些常用的路径,在离线下算好放在数据文件中,游戏开启时放在内存里,当需要寻路到那个节点或者那个节点附近时,就可以取出来直接使用,而不再需要计算。可以尝试做一些导航点,当开始计算寻路时优先找到最近的导航点,之后只需要进行到达导航点的路径就可以了

二.优化预测值计算方法

如果预测值只关注在于终点距离最近的点上,那么在寻路过程中的选择点位的顺序就会偏向于与终点更近的点。而如果预测值计算公式,关注的是整个距离较近的点位上,那么在寻路过程中在选择点位上也就会偏向整条路径短的方向上去靠。

预测值算法有很多种,例如 直来直去,可以斜着走,还有按三角形走等

三.排序算法的优化

每次从OpenList中取出最小值时,可以用排序算法事先将集合排序好,这样节省了查找的时间,再每次插入的时候有序的插入.

也可以考虑采用二叉树平衡树结构进行存

四.CloseList的优化

实际上这个集合我们所用到的地方只有判断是否走过这一个地方,为了避免频繁判断是否包含节点(因为每次判断都需要遍历一遍集合),可以给节点添加一个属性 IsClose ,属性设置为int 之后寻路系统中声明一个静态变量 用于比较是否被CloseSet包含,如果相等证明包含,不包含加到Close中,并将值付给节点,每次寻路将静态变量++,这样虽然会多占用写内存,但是大大的节省了我们计算的时间

五.更详细的A星算法

这个是一名抠门的巨佬的笔记

实战

在一个 3×3 的网格中,1∼8 这 8 个数字和一个 X 恰好不重不漏地分布在这 3×3 的网格中。

例如:

1 2 3
X 4 6
7 5 8
在游戏过程中,可以把 X 与其上、下、左、右四个方向之一的数字交换(如果存在)。 我们的目的是通过交换,使得网格变为如下排列(称为正确排列): 1 2 3
4 5 6
7 8 X
例如,示例中图形就可以通过让 X 先后与右、下、右三个方向的数字交换成功得到正确排列。 交换过程如下: 1 2 3 1 2 3 1 2 3 1 2 3
X 4 6 4 X 6 4 5 6 4 5 6
7 5 8 7 5 8 7 X 8 7 8 X
把 X 与上下左右方向数字交换的行动记录为 u、d、l、r。 现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。 输入格式
输入占一行,将 3×3 的初始网格描绘出来。 例如,如果初始网格如下所示: 1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8 输出格式
输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。 如果答案不唯一,输出任意一种合法方案即可。 如果不存在解决方案,则输出 unsolvable。 输入样例:
2 3 4 1 5 x 7 6 8
输出样例
ullddrurdllurdruldr #include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <unordered_map> using namespace std; int f(string state)
{
int res = 0;
for (int i = 0; i < state.size(); i ++ )
if (state[i] != 'x')
{
int t = state[i] - '1';
res += abs(i / 3 - t / 3) + abs(i % 3 - t % 3);
}
return res;
} string bfs(string start)
{
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char op[4] = {'u', 'r', 'd', 'l'}; string end = "12345678x";
unordered_map<string, int> dist;
unordered_map<string, pair<string, char>> prev;
priority_queue<pair<int, string>, vector<pair<int, string>>, greater<pair<int, string>>> heap; heap.push({f(start), start});
dist[start] = 0; while (heap.size())
{
auto t = heap.top();
heap.pop(); string state = t.second; if (state == end) break; int step = dist[state];
int x, y;
for (int i = 0; i < state.size(); i ++ )
if (state[i] == 'x')
{
x = i / 3, y = i % 3;
break;
}
string source = state;
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < 3 && b >= 0 && b < 3)
{
swap(state[x * 3 + y], state[a * 3 + b]);
if (!dist.count(state) || dist[state] > step + 1)
{
dist[state] = step + 1;
prev[state] = {source, op[i]};
heap.push({dist[state] + f(state), state});
}
swap(state[x * 3 + y], state[a * 3 + b]);
}
}
} string res;
while (end != start)
{
res += prev[end].second;
end = prev[end].first;
}
reverse(res.begin(), res.end());
return res;
} int main()
{
string g, c, seq;
while (cin >> c)
{
g += c;
if (c != "x") seq += c;
} int t = 0;
for (int i = 0; i < seq.size(); i ++ )
for (int j = i + 1; j < seq.size(); j ++ )
if (seq[i] > seq[j])
t ++ ; if (t % 2) puts("unsolvable");
else cout << bfs(g) << endl; return 0;
} 给定一张 N 个点(编号 1,2…N),M 条边的有向图,求从起点 S 到终点 T 的第 K 短路的长度,路径允许重复经过点或边。 注意: 每条最短路中至少要包含一条边。 输入格式
第一行包含两个整数 N 和 M。 接下来 M 行,每行包含三个整数 A,B 和 L,表示点 A 与点 B 之间存在有向边,且边长为 L。 最后一行包含三个整数 S,T 和 K,分别表示起点 S,终点 T 和第 K 短路。 输出格式
输出占一行,包含一个整数,表示第 K 短路的长度,如果第 K 短路不存在,则输出 −1。 数据范围
1≤S,T≤N≤1000,
0≤M≤104,
1≤K≤1000,
1≤L≤100
输入样例:
2 2
1 2 5
2 1 4
1 2 2
输出样例:
14 #include <cstring>
#include <iostream>
#include <algorithm>
#include <queue> #define x first
#define y second using namespace std; typedef pair<int, int> PII;
typedef pair<int, PII> PIII; const int N = 1010, M = 200010; int n, m, S, T, K;
int h[N], rh[N], e[M], w[M], ne[M], idx;
int dist[N], cnt[N];
bool st[N]; void add(int h[], int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
} void dijkstra()
{
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, T}); memset(dist, 0x3f, sizeof dist);
dist[T] = 0; while (heap.size())
{
auto t = heap.top();
heap.pop(); int ver = t.y;
if (st[ver]) continue;
st[ver] = true; for (int i = rh[ver]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});
}
}
}
} int astar()
{
priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
heap.push({dist[S], {0, S}}); while (heap.size())
{
auto t = heap.top();
heap.pop(); int ver = t.y.y, distance = t.y.x;
cnt[ver] ++ ;
if (cnt[T] == K) return distance; for (int i = h[ver]; ~i; i = ne[i])
{
int j = e[i];
if (cnt[j] < K)
heap.push({distance + w[i] + dist[j], {distance + w[i], j}});
}
} return -1;
} int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
memset(rh, -1, sizeof rh); for (int i = 0; i < m; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(h, a, b, c);
add(rh, b, a, c);
}
scanf("%d%d%d", &S, &T, &K);
if (S == T) K ++ ; dijkstra();
printf("%d\n", astar()); return 0;
}

广度优先搜索— —提高Ⅲ(BFS优化)的更多相关文章

  1. 深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现

    1.基础部分 在图中实现最基本的操作之一就是搜索从一个指定顶点可以到达哪些顶点,比如从武汉出发的高铁可以到达哪些城市,一些城市可以直达,一些城市不能直达.现在有一份全国高铁模拟图,要从某个城市(顶点) ...

  2. 深度优先搜索(DFS)和广度优先搜索(BFS)

    深度优先搜索(DFS) 广度优先搜索(BFS) 1.介绍 广度优先搜索(BFS)是图的另一种遍历方式,与DFS相对,是以广度优先进行搜索.简言之就是先访问图的顶点,然后广度优先访问其邻接点,然后再依次 ...

  3. 队列的JS实现及广度优先搜索(BFS)的实现

    队列是先进先出(FIFO)的数据结构,插入操作叫做入队,只能添加在队列的末尾:删除操作叫做出队,只能移除第一个元素.在JS中,用数组可以很简单的实现队列.JavaScript实现排序算法 functi ...

  4. Leetcode之广度优先搜索(BFS)专题-详解429. N叉树的层序遍历(N-ary Tree Level Order Traversal)

    Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary Tree Level Order Traversal) 给定一个 N 叉树,返回其节点值的层序遍历. (即从左到右 ...

  5. Z1. 广度优先搜索(BFS)解题思路

    /** BFS 解题思路 特点:从某些特定的节点开始,感染相邻的节点; 被感染的节点,再感染其相邻的节点,以此类推. 题目常见于数据结构包括 二维数组.树.图 **/ /** 1). 二维数组特定节点 ...

  6. Leetcode之广度优先搜索(BFS)专题-773. 滑动谜题(Sliding Puzzle)

    Leetcode之广度优先搜索(BFS)专题-773. 滑动谜题(Sliding Puzzle) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary ...

  7. Leetcode之广度优先搜索(BFS)专题-127. 单词接龙(Word Ladder)

    Leetcode之广度优先搜索(BFS)专题-127. 单词接龙(Word Ladder) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary Tre ...

  8. Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock)

    Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary ...

  9. Leetcode之广度优先搜索(BFS)专题-1162. 地图分析(As Far from Land as Possible)

    Leetcode之广度优先搜索(BFS)专题-1162. 地图分析(As Far from Land as Possible) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. ...

  10. Leetcode之广度优先搜索(BFS)专题-994. 腐烂的橘子(Rotting Oranges)

    Leetcode之广度优先搜索(BFS)专题-994. 腐烂的橘子(Rotting Oranges) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ar ...

随机推荐

  1. ip2location.py

    import re import geoip2.database from tabulate import tabulate from matplotlib import pyplot as plt ...

  2. Print, printf, println的区别

    print 非格式,打印变量的值,不换行 printf 支持格式化输出,不换行 println 非格式,打印变量的值 ,换行

  3. torch.nn基础学习教程 | PyTorch nn Basic Tutorial

    基于torch.nn搭建神经网络的基础教程大纲: 1. 引言 在我们开始深入探讨torch.nn之前,我们首先需要理解PyTorch及其神经网络库的基础知识.这一部分的内容将帮助你对PyTorch有一 ...

  4. 聊一聊 Go 的内存对齐

    前言 在一次工作中,需要使用 Go 调用 DLL 文件,其中就涉及到内存对齐的相关知识,如果自定义的结构体内存布局和所调用的 DLL 结构体内存布局不一致,就会无法正确调用.所以,一旦涉及到较为底层的 ...

  5. PHP调用API接口的方法及实现

    随着互联网.云计算和大数据时代的到来,越来越多的应用程序需要调用第三方的API接口来获取数据,实现数据互通和协同工作.PHP作为一种常用的服务器端语言,也可以通过调用API接口来实现不同系统的数据交互 ...

  6. WPF学习 - 动画基础(1)

    1. WPF中的动画(Animation),是一种属性动画.技术上来说,它是让属性从一个值,变化到另一个值的过程.因此,有两条重要的特性: 1.1 只能为依赖属性应用动画(因为第二条特性). 1.2 ...

  7. 关于前后端交互,取header的尴尬

    背景: 最近在写一个接口的时候,需求是这样的,上传excel,匹配项目有多少个字段匹配上了,如果匹配上了在单元格上标注绿色背景,然后返回excel文件和匹配的详细. 首先这个excel文件,后端是不会 ...

  8. [错误] SQL logic error near "date": syntax error

    问题的来源 今天把一个项目的数据库从MySQL改到Sqlite 调试时发生了这个错误. 百度又看不懂英文(很多是国外发的), 就折腾了一下 原因 C# Sqlite 不能使用参数前缀"?&q ...

  9. 8K Star,一款开源仿Notion且AI强化的编辑器:Novel

    Notion相信大家都不陌生了,一款非常好用的笔记软件,TJ君也一直在用来记笔记和写文章.关于Notion的替代品,之前有给大家推荐AFFiNE ,但这个还是一个比较成型的软件. 那么如果想开发一个类 ...

  10. RCU的简单认识

    RCU RUC是什么? RCU(Read-Copy-Update)是一种用于并发编程的技术,旨在提供高效且无锁(lock-free)的读操作,同时保证数据一致性和并发性. 也就是说他并不需要锁的机制来 ...