淘淘蓝蓝的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