// Slash Maze (斜线迷宫)
// PC/UVa IDs: 110904/705, Popularity: B, Success rate: average Level: 2
// Verdict: Accepted
// Submission Date: 2011-09-28
// UVa Run Time: 0.012s
//
// 版权所有(C)2011,邱秋。metaphysis # yeah dot net
//
// [问题描述]
// 通过用斜线(/)和反斜线(\)来填充一个矩形,你可以得到一个小迷宫,例如:
//
// (由于 gedit 不能插入图片,请参考 UVa 上原题的图片,原题链接:http://uva.onlinejudge.o
// rg/index.php?option=com_onlinejudge&Itemid=8&category=37&page=show_problem&pr
// oblem=646。)
//
// 正如你所看到的,迷宫中的通路从不分叉,因此整个迷宫中只有(1)环状通路以及(2)从一个地方进入并
// 从另外一个地方出去的通路。我们只对环感兴趣。在上述例子中有两个环。
//
// 你的任务是要写一个程序来计算最长的环的长度。环的长度被定义为组成这个环的小正方形(在图中用灰色
// 线条标记)的数量。在上述例子中,长环的长度为 16,短环的长度为 4。
//
// [输入]
// 输入包含若干组数据。每组数据的第一行两个整数 w 和 h(1 <= w,h <= 75),分别代表迷宫的宽度
// 和高度。接下来的 h 行,每行有 w 个字符,描述整个迷宫。这些字符都是“/”或“\”。
//
// 当 w = h = 0 时输入结束。这一行不必处理。
//
// [输出]
// 对于每个迷宫,先输出一行“Maze #n:”,其中 n 为迷宫序号。然后输出一行“k Cycles; the longest
// has length l.“,其中 k 为迷宫中环的数量,l 是最长环的长度。如果这个迷宫中没有环,输出”There
// are no cycles.“
//
// 在每组数据的输出后打印一个空行。
//
// [样例输入]
// 6 4
// \//\\/
// \///\/
// //\\/\
// \/\///
// 3 3
// ///
// \//
// \\\
// 0 0
//
// [样例输出]
// Maze #1:
// 2 Cycles; the longest has length 16.
//
// Maze #2:
// There are no cycles.
//
// [解题方法]
// 若是正常的迷宫,横平竖直的自然是比较好解决的,但是该题是用斜线表示的迷宫,初看不是很好处理,实
// 际上将斜线和反斜线构成的迷宫所隐含的图显式的构建出来,转换成直线型的迷宫,然后使用图遍历的算法
// 或者 Flood fill 算法或 Union-Find 算法计算最大环长度。
//
// 观察题目所给的图可以知道,斜线和反斜线占据了两个小正方形的边,那么可以将斜线占据的边使用 1 来
// 表示,反斜线占据的边用 2 来表示,未占据的边使用 0 来表示,那么题目所给的图转换成如下的矩阵:
//
// 200101202001
// 021010020210
// 200101012001
// 021010100210
// 010120200120
// 101002021002
// 200120010101
// 021002101010
//
// 问题转化为在此矩阵中寻找从值为 0 的位置出发能够回到出发位置的最长路径。先从矩阵的最外边寻找为
// 0 的位置,使用 Flood fill 算法将与该 0 位置连通的位置全部置为 3,表示这些点已经处理掉,因为
// 这些位置是不可能构成环的,这个是很明显的(不明白的话再想一下...)。将矩阵周边的 0 位置全部处
// 理掉后,矩阵变成:
//
// 233131232331
// 321310023213
// 233101012331
// 321010100213
// 310120200123
// 101002021332
// 200120013131
// 321332131313
//
// 经过处理后,所有不能够成环的 0 位置都已经处理掉了,此时从矩阵中任意选择一个 0 位置,使用
// Flood fill 算法遍历与该 0 位置能连通的所有位置,并将其置为 3,记录这个过程中找到 0 的次数,
// 即为该环的长度。
//
// 唯一需要注意的是在遍历时方向的选择问题,由于已经将正反斜线予以标记,只需朝位置为 0 的方向走即可,
// 在走的过程需要依据当前相邻的正反斜线决定可行的方向。
//
// Flood fill 算法介绍:
// http://en.wikipedia.org/wiki/Flood_fill。
//
// Union-Find 算法介绍:
// http://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf。
#include <iostream>
using namespace std;
#define MAXV 150
#define EMPTY 0
#define SLASH 1
#define BACKSLASH 2
#define VISITED 3
#define LEFT_UP 0
#define UP 1
#define RIGHT_UP 2
#define LEFT 3
#define RIGHT 4
#define LEFT_BOTTOM 5
#define DOWN 6
#define RIGHT_BOTTOM 7
struct mazes
{
int status[MAXV][MAXV];
int width, height;
};
int length;
int offset[8][2] = { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0},
{1, 1} };
// 检查坐标是否在矩阵内。
inline bool in_range(int line, int row, int width, int height)
{
return ((0 <= line && line < height) && (0 <= row && row < width));
}
// 使用 Flood fill 算法将与位置(line,row)相连通的位置其值置为 VISITED。Flood fill 算法
// 实际上是深度优先搜索的思想。
void flood_fill(mazes * maze, int line, int row)
{
length++;
maze->status[line][row] = VISITED;
for (int d = LEFT_UP; d <= RIGHT_BOTTOM; d++)
{
int tline = line + offset[d][0];
int trow = row + offset[d][1];
if (in_range(tline, trow, maze->width, maze->height))
if (maze->status[tline][trow] == EMPTY)
{
// 左,右,上,下的 0 位置总是可以直接走的。
if (tline == line || trow == row)
flood_fill(maze, tline, trow);
// 对角线上的 0 位置才需要判断。
else
{
// 左上角的 0 位置可行的条件是其左侧不为斜线。
if (d == LEFT_UP)
if (maze->status[line][row - 1] != SLASH)
flood_fill(maze, tline, trow);
// 右下角的 0 位置可行的条件是其右侧不为斜线。
if (d == RIGHT_BOTTOM)
if (maze->status[line][row + 1] != SLASH)
flood_fill(maze, tline, trow);
// 左下角的 0 位置可行的条件是其左侧不为反斜线。
if (d == LEFT_BOTTOM)
if (maze->status[line][row - 1] != BACKSLASH)
flood_fill(maze, tline, trow);
// 右上角的 0 位置可行的条件是其右侧不为反斜线。
if (d == RIGHT_UP)
if (maze->status[line][row + 1] != BACKSLASH)
flood_fill(maze, tline, trow);
}
}
}
}
int main(int ac, char *av[])
{
mazes maze;
int width, height, cases = 1;
char slash;
bool is_backslash;
while (cin >> width >> height, width && height)
{
// 注意在读入数据时是将给定迷宫长和宽各扩大两倍来表示。
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
{
cin >> slash;
is_backslash = (slash == '\\');
maze.status[i * 2][j * 2] = is_backslash ? BACKSLASH : EMPTY;
maze.status[i * 2][j * 2 + 1] = is_backslash ? EMPTY : SLASH;
maze.status[i * 2 + 1][j * 2] = is_backslash ? EMPTY : SLASH;
maze.status[i * 2 + 1][j * 2 + 1] = is_backslash ? BACKSLASH : EMPTY;
}
maze.width = 2 * width;
maze.height = 2 * height;
// 处理矩阵周边的 0。
for (int i = 0; i < maze.height; i++)
for (int j = 0; j < maze.width; j++)
if (maze.status[i][j] == EMPTY)
if (i == 0 || j == 0 || i == (maze.height - 1)
|| j == (maze.width - 1))
flood_fill(&maze, i, j);
// 获取最大环长度。
int maximum = 0, cycles = 0;
for (int i = 0; i < maze.height; i++)
for (int j = 0; j < maze.width; j++)
if (maze.status[i][j] == EMPTY)
{
cycles++;
length = 0;
flood_fill(&maze, i, j);
if (maximum < length)
maximum = length;
}
// 输出结果,注意每组数据后需要输出空行。
cout << "Maze #" << cases++ << ":" << endl;
if (maximum > 0)
{
cout << cycles;
cout << " Cycles; the longest has length ";
cout << maximum << ".";
}
else
cout << "There are no cycles.";
cout << endl << endl;
}
return 0;
}
UVa Problem 705 Slash Maze (斜线迷宫)
最新推荐文章于 2024-05-13 09:33:08 发布