UVa Problem 705 Slash Maze (斜线迷宫)

// 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;
}








你可能感兴趣的:(c,算法,Date,struct,UP,任务)