C++ 广度优先搜索的标记策略(五十六)【第三篇】

今天我们来看看bfs是如何规划标记策略的。

1.标记策略

但先等一下,先看一道题《一维坐标的移动》

在一个长度为 n 的坐标轴上,蒜头君想从 A 点 移动到 
B 点。他的移动规则如下:

  1. 向前一步,坐标增加 
    1。

  2. 向后一步,坐标减少 1。

  3. 跳跃一步,使得坐标乘 
    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 操作了。

你可能感兴趣的:(C++,算法)