淘淘蓝蓝的CSP-S神妙膜你赛2-淘淘蓝蓝之树林 题解

题目描述

淘淘和蓝蓝遇到了一片树林,他们想知道,最少几步能在不进入树林的情况下走一圈,最后回到他们的起点。淘淘和蓝蓝所在区域的地图可以抽象成一个\(n*m\)的矩阵,其中有树林和空地。淘淘和蓝蓝的起点是一个空地。树林是一个上下左右四联通的区域,并且它的内部没有空地。淘淘和蓝蓝可以上下左右或斜向八联通行走。他们可以不一定沿着树林边缘行走,但最后行走的路径一定是一个包住树林的
\(X\)表示树林,\(*\)表示淘淘和蓝蓝的起点。保证存在最短路径。

数据范围

对于\(100\)%的数据:\(n,m\le2000\)

思路

如果我们直接从起点出发绕着森林寻找最短路径,很难实现一个环状的路径。

而这题的巧妙之处,在于将环展开变成链。
我们可以在森林的一边人为造一堵墙,使得直接走不能走过去。
然后我们从起点对整张图进行宽搜,找到到达每个点的最短路。
再把到墙的的两条路径相加再加上\(2(\)走进去走出来\()\)就是从起点绕森林一圈的最短路了

代码:

#include <bits/stdc++.h>
using namespace std;
#define MAXR 2200
#define MAXC 2200
static int R, C;
static int Sr = -1, Sc = -1;
static int Ir = -1, Ic = -1;
static char grid[MAXR][MAXC + 1];
static int label[MAXR][MAXC];
static int Z;
static const int dr[8] = {-1, 0, 1, 0, -1, -1, 1, 1};
static const int dc[8] = {0, -1, 0, 1, -1, 1, -1, 1};
static void label_dfs(int r, int c, int l, bool diag) {
    int m = diag ? 8 : 4;
    label[r][c] = l;
    for (int d = 0; d < m; d++) {
        int r2 = r + dr[d];
        int c2 = c + dc[d];
        if (r2 >= 0 && r2 < R && c2 >= 0 && c2 < C
            && grid[r2][c2] == grid[r][c] && label[r2][c2] != label[r][c])
            label_dfs(r2, c2, l, diag);
    }
}
static void validate() {
    memset(label, 0, sizeof(label));
    label_dfs(Sr, Sc, 1, true);
    label_dfs(Ir, Ic, 2, false);
    for (int i = 0; i < R; i++)
        for (int j = 0; j < C; j++)
        {
            if (i == 0 || i == R - 1 || j == 0 || j == C - 1)
                assert(label[i][j] == 1);
            if (grid[i][j] == 'X') assert(label[i][j] == 2);
        }
}
struct point {
    int r, c;
    int wind;
    point() {}
    point(int r, int c, int w) : r(r), c(c), wind(w) {}
};
static void solve() {
    queue<point> q;
	int prio[2001][2001][2];
	point parent[2001][2001][2];

    for (int i = 0; i < R; i++)
        for (int j = 0; j < C; j++)
            prio[i][j][0] = prio[i][j][1] = INT_MAX;
    prio[Sr][Sc][0] = 0;
    q.push(point(Sr, Sc, 0));
    while (!q.empty()) {
        point cur = q.front();
        int p = prio[cur.r][cur.c][cur.wind];
        q.pop();
        for (int d = 0; d < 8; d++) {
            point nxt(cur.r + dr[d], cur.c + dc[d], cur.wind);
            if (nxt.r < 0 || nxt.r >= R || nxt.c < 0 || nxt.c >= C) continue;
            if (grid[nxt.r][nxt.c] == 'X') continue;
            if (cur.r == Ir - 1 && nxt.r == Ir && nxt.c < Ic) continue;
            if (cur.r == Ir && nxt.r == Ir - 1 && cur.c < Ic) {
                nxt.wind++;
                if (nxt.wind > 1) continue;
            }
            if (p + 1 < prio[nxt.r][nxt.c][nxt.wind]) {
                prio[nxt.r][nxt.c][nxt.wind] = p + 1;
                q.push(nxt);
                parent[nxt.r][nxt.c][nxt.wind] = cur;
            }
        }
    }
    Z = prio[Sr][Sc][1];
    assert(Z != INT_MAX);
    point cur(Sr, Sc, 1);
    while (cur.r != Sr || cur.c != Sc || cur.wind != 0) {
        cur = parent[cur.r][cur.c][cur.wind];
        grid[cur.r][cur.c] = '+';
    }
    grid[Sr][Sc] = '*';
}
int main()
{
	freopen("forest.in" , "r" , stdin);
	freopen("forest.out" , "w" , stdout);
    cin >> R >> C;
    for (int i = 0; i < R; i++) {
        cin >> grid[i];
        assert(strlen(grid[i]) == (size_t) C);
        for (int j = 0; j < C; j++) {
            if (grid[i][j] == '*') {
                assert(Sr == -1);
                Sr = i;
                Sc = j;
                grid[i][j] = '.';
            }
            assert(grid[i][j] == '.' || grid[i][j] == 'X');
            if (grid[i][j] == 'X' && Ir == -1) {
                Ir = i;
                Ic = j;
            }
        }
    }
    validate();
    solve();
    cout << Z << "\n";
    return 0;
}

程序来自std

posted @ 2022-07-22 16:09  Yoican  阅读(126)  评论(2编辑  收藏  举报