题解:P2864 [USACO06JAN] The Grove S

· · 题解

题目传送门

题目大意

这道题目言简意赅,就是要我们求以 * 为起点绕 X 走一圈的最短路。

思路分析

下面介绍 建墙法。
以一个简单的样例为例:

.......
...X...
..XXX..
...XXX.
...X...
......*

最后一棵树为原点,向下建墙。

最后一棵树 XX 坐标 +1 记为 ltxY 记为 lty

注:X 坐标需 +1,以不与树重合。

如下所示:

.......
...X...
..XXX..
...XXX.
...X...
...|..*
建好墙后,以贝茜为原点,进行 BFS。 所走的步数如下所示: ```cpp 8 7 6 5 5 5 5 8 7 6 X 4 4 4 8 7 X X X 3 3 8 8 8 X X X 2 9 9 9 X 2 1 1 10 10 10 | 2 1 0 ``` BFS 后,就是统计答案了! 那么答案记为 $sum$,统计步数的数组为 $ans$。 $sum=\min(ans_{ltx−1,lty−1},ans_{ltx,lty−1},ans_{ltx+1,lty−1})+\min(ans_{ltx−1,lty+1},ans_{ltx,lty+1},ans_{ltx+1,lty+1})+2

tips

  1. 墙不能与贝茜重合
  2. BFS 时注意墙与树 一样不能走。
  3. BFS 注意边界。
  4. 统计过的点无需再次访问。
  5. 墙不能与树重合。

    附上 AC 代码

    #include<cstdio>
    #include<iostream>
    using namespace std;
    #define ll long long
    #define INF 0x7f7f7f7f7f
    ll min(ll a, ll b) {//手打min
    return a < b ? a : b;
    }
    ll r, c; //见题面,r:长 c:宽
    char a[100][100]; //地图
    ll sx, sy, ltx, lty; //*的坐标,及最后一棵树的坐标
    int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1}, dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; //8个方向
    ll ans[110][110]; //见前文
    ll q[100100][3], h, t; //手打BFS队列
    int main() {
    cin >> r >> c; //读入
    for (int i = 1; i <= r; i++) {
        for (int j = 1; j <= c; j++) {
            cin >> a[i][j];
            if (a[i][j] == '*')sx = i, sy = j; //*坐标
            else if (a[i][j] == 'X')ltx = i + 1, lty = j; //最后一棵树的坐标
        }
    }
    for (int i = 0; i < 110; i++)for (int j = 0; j < 110; j++)ans[i][j] = INF; //ans赋值为最大值
    if (ltx == sx && lty == sy) { //特判,墙不能与*重合
        bool f = 0;
        for (int i = 1; i <= r; i++){
            for (int j = 1; j <= c; j++){
                if (a[i][j] == 'X' && !f) {
                    ltx = i;
                    lty = j;
                    f = 1;
                    break;
                }
            }
        }
        for (int i = 1; i <= ltx; i++)a[i][lty] = '|';
    }
    for (int i = ltx; i <= r; i++)a[i][lty] = '|';
    
    ll x, y, s, xx, yy; //BFS开始
    ans[sx][sy] = 0;
    t = 1;
    q[0][0] = sx;
    q[0][1] = sy;
    while (h < t) {
        x = q[h][0], y = q[h][1], s = q[h][2]; //取队首
        for (int i = 0; i < 8; i++) { //8个方向搜
            xx = x + dx[i], yy = y + dy[i]; //替死鬼
            if (ans[xx][yy] != INF)continue; //不重复搜
            if ((xx >= 1 && xx <= r && yy >= 1 && yy <= c) && a[xx][yy] == '.') { //边界及能否走
                ans[xx][yy] = s + 1; //记入答案
                q[t][0] = xx; //入队
                q[t][1] = yy; //入队
                q[t++][2] = s + 1; //入队
            }
        }
        h++; //出队
    } //BFS结束
    
    cout << min(ans[ltx - 1][lty - 1], min(ans[ltx][lty - 1], ans[ltx + 1][lty - 1])) + min(ans[ltx - 1][lty + 1], min(ans[ltx][lty + 1], ans[ltx + 1][lty + 1])) + 2; //输出答案
    return 0;
    }