算法提高课 -- 12

最短路模型

这就没什么新颖的东西了,清一色的BFS就行。

BFS函数的一般模板(以二维平面举例):

// 存入初始下标x, y
int bfs(int x, int y) {
    pair<int, int> q[N]; // 设置队列 数据结构写法为 queue<int> q;
    int hh = 0, tt = 0; // 初始化队头和队尾
    q[0] = {x, y}; // hh = tt = 0,说明初始下标需要预先存入队列 数据结构写法为 q.push({x, y});
    memset(dist, -1, sizeof dist); // 初始化dist距离数组为-1,dist[i][j] 表示从起点到(i, j)的最短距离还没有算过
    while (hh <= tt) { // 若队列中还有元素则进行循环,数据结构写法为 !q.empty();
        PII t = q[hh++]; // 取出队头 数据结构写法为 auto t = q.front(), q.pop();
        /*各种判断、位置移动的操作*/
    }
    return dist[end_x][end_y]; // 输出起点到终点的最短距离
}

例题1、Acwing 188.武士风度的牛

很经典的最短路问题,在BFS函数中用队列存入每一次能走到的点对其进行判断是否合法直至到达终点输出最小值即可;队列可用数组模拟pair<int, int> q[N];也可用数据结构queue<pair<int, int>> q;

代码展示(数组模拟队列):

#include <iostream>
#include <algorithm>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 160;
int n, m;
char g[N][N], dist[N][N];
int begin_x, begin_y;
int dx[8] = {-1, 1, 2, 2, 1, -1, -2, -2}, dy[8] = {2, 2, 1, -1, -2, -2, -1, 1};
int bfs(int x, int y) {
    PII q[N * N];
    int hh = 0, tt = 0;
    q[0] = {x, y};
    memset(dist, -1, sizeof dist);
    dist[x][y] = 0;
    while (hh <= tt) {
        PII t = q[hh++];
        if (g[t.x][t.y] == 'H') return dist[t.x][t.y];
        for (int i = 0; i < 8; i++) {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 0 || a >= n || b < 0 || b >= m) continue;
            if (g[a][b] == '*' || dist[a][b] != -1) continue;
            dist[a][b] = dist[t.x][t.y] + 1;
            q[++tt] = {a, b};
        }
    }
    return -1;
}
int main() {
    scanf("%d%d", &m, &n);
    for (int i = 0; i < n; i++) scanf("%s", &g[i]);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (g[i][j] == 'K') {
                begin_x = i, begin_y = j;
                break;
            }
        }
    }
    printf("%d\n", bfs(begin_x, begin_y));
    return 0;
}

例题2、Acwing 1076.迷宫问题

这里是一个很经典的拓展:输出最短路径而不是最短路径长。正常的BFS只是输出最短长度,若要输出路径则需要一个容器记录路径:pair<int, int> \ path[1010][1010],表示的含义就是该点在最短长度这条路径中的前一个点;在正常BFS做完之后,由于答案要求从前往后输出,因此可以正向搜索、递归输出,也可以倒叙搜索、正向输出

代码展示(正向搜索、递归输出):

#include <iostream>
#include <algorithm>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int n;
int w[N][N];
PII path[N][N]; // path[i][j] 表示最短路线中(i, j)的前一个,记录路径
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
void bfs(int x, int y) {
    memset(path, -1, sizeof path);
    PII q[N * N];
    int hh = 0, tt = 0;
    q[0] = {x, y};
    while (hh <= tt) {
        PII t = q[hh++];
        for (int i = 0; i < 4; i++) {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 0 || a >= n || b < 0 || b >= n) continue;
            if (w[a][b] || path[a][b].x != -1) continue;
            q[++tt] = {a, b};
            path[a][b] = t;
        }
    }
}
void dfs_print(int x, int y) {
    if (x == 0 && y == 0) {
        puts("0 0");
        return;
    }
    dfs_print(path[x][y].x, path[x][y].y);
    printf("%d %d\n", x, y);
}
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &w[i][j]);
        }
    }
    bfs(0, 0); // 正序遍历
    dfs_print(n - 1, n - 1); // 逆序搜索输出
    return 0;
}

上面的两道题都是比较简单的二维最短路问题,而例题3、Acwing 1100. 抓住那头牛则是一个一维的最短路问题,这里需要修改一点:在二维中的状态转移都是根据坐标的变化模拟出dx[]和dy[]位移数组,而在这里题目中只给出了三种特殊的移动方式,因此需要依次分析其移动情况。

代码展示:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010;
int n, k;
int dist[N];
int bfs(int begin) {
    int q[N];
    int hh = 0, tt = 0;
    q[0] = begin;
    memset(dist, -1, sizeof dist);
    dist[begin] = 0;
    while (hh <= tt) {
        int t = q[hh++];
        // 依次分析三种移动方式
        // * 2
        if (t * 2 < N && dist[2 * t] == -1) {
            q[++tt] = t * 2;
            dist[2 * t] = dist[t] + 1;
        }
        // + 1
        if (t + 1 < N && dist[t + 1] == -1) {
            q[++tt] = t + 1;
            dist[t + 1] = dist[t] + 1;
        }
        // - 1
        if (t - 1 >= 0 && dist[t - 1] == -1) {
            q[++tt] = t - 1;
            dist[t - 1] = dist[t] + 1;
        }
    }
    return dist[k];
}
int main() {
    scanf("%d%d", &n, &k);
    printf("%d\n", bfs(n));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值