今天我们来看看bfs是如何规划标记策略的。
1.标记策略
但先等一下,先看一道题《一维坐标的移动》
在一个长度为 n 的坐标轴上,蒜头君想从 A 点 移动到
B 点。他的移动规则如下:
向前一步,坐标增加
1。
向后一步,坐标减少 1。
跳跃一步,使得坐标乘
2。
蒜头君不能移动到坐标小于 0 或大于
n 的位置。蒜头君想知道从
A 点移动到 B 点的最少步数是多少,你能帮他计算出来么?
初始代码中已经写好了一些代码,不可更改,大家只需要完成 bfs 函数部分即可。
第一行输入三个整数
n,A,
B,分别代表坐标轴长度,起始点坐标,终点坐标。(
0≤A,B≤n≤5000)
输出一个整数占一行,代表蒜头要走的最少步数。
这道题也就是套个模板:
#include
#include
#include
using namespace std;
int n, A, B;
int dis[5005];
queue q;
void bfs(int A) {
memset(dis, -1, sizeof(dis));
dis[A] = 0;
q.push(A);
while (!q.empty()) {
int now = q.front();
q.pop();
if (now + 1 <= n && dis[now + 1] == -1) {
dis[now + 1] = dis[now] + 1;
q.push(now + 1);
}
if (now - 1 >= 0 && dis[now - 1] == -1) {
dis[now - 1] = dis[now] + 1;
q.push(now - 1);
}
if (now * 2 <= n && dis[now * 2] == -1) {
dis[now * 2] = dis[now] + 1;
q.push(now * 2);
}
}
}
int main() {
freopen("move.in", "r", stdin);
freopen("move.out", "w", stdout);
cin >> n >> A >> B;
bfs(A);
cout << dis[B] << endl;
return 0;
}
在之前遇到的问题中,将已经到达的状态使用布尔数组或整数数组进行标记,从而避免 重复访问 以及得到 最少步数 等目的。
很明显,之前遇到的一些问题,其状态的空间均为连续的一维空间或二维空间。相应的在标记时,创建一维或二维数组进行标记非常方便。
然而在一些情况下,当前的状态难以简单的标记,如当前状态为稀疏的一维数轴上的点,数组开不下,甚至当前状态可能是一个字符串,根本无法开出标记数组等。那么对于这些情况,可以进行怎样的标记,使得达到与仅创建数组相同的效果呢?
如在 《一维坐标的移动》 一题中,假设需要起点或终点的位置非常大,如 1≤A,B≤10的9次方。此时虽然通过 ×2 操作也能够很快搜索到答案,但是由于 A,B 范围过大,不能开辟对应大小的数组,为解决这样的情况带来问题。
此时我们可以利用 set 或 map 来进行标记。
set可以起到vis数组的作用,不在set内就代表访没访问过,map可以起到dis数组的作用,记录下来到每种状态的最小步数。
不过在一些情况下我们需要将创建的结构体放入 set 或 map 中,此时就需要重载结构体的小于号了,我们来回顾一下。
下面是一个具体的例子,在一个拥有 x, y 两个属性的结构体 点 中,首先比较
x 值大小,如果相同就比较 y 值大小:
struct Point{
int x, y;
bool operator < (const Point& rhs) const {
if (x != rhs.x) {
return x < rhs.x;
}
return y < rhs.y;
}
};
重载了小于号,该结构体就能够放入 set 或 map 中进行标记,以进行后续的 BFS 操作了。