由一系列的左斜杠(/)和右斜杠(\)组成的围墙将一个由n * m个方格构成的矩形分割成若干个区域。每个区域由若干个三角形构成,如图8-8所示。如果一个人从一个三角形区域经过其相邻的三角形区域一次最后再回到起始出发的三角形,则其走过的所有的三角形区域构成一个回路。设每个三角形区域的长度为1,求迷宫中长度最长的回路的长度。图中最长回路的长度为8。
经分析,由于该迷宫的特殊性,一个区域内最多只有一条回路。因此大大简化了算法复杂性。
在初始时仅需输入左右斜杠,如下所示:
但实际上,我们会先建立一个4*12的矩阵来表示迷宫,为什么是4*12而不是4*4,因为 一个斜杠必然伴随着两个三角,因此矩阵的列会乘以3,然后在输入的时候,每行的第一个斜杠纵坐标必然是1,此后下一个斜杠的纵坐标是前一个纵坐标加3,因此初始化矩阵代码如下:
int i, j;
cin >> width >> length;
char**maze;
length *= 3;
maze = new char* [width];
for (i = 0;i < width;++i) {
maze[i] = new char[length];
memset(maze[i], '0', sizeof(char) * length);
}
for (i = 0;i < width;++i)
for (j = 1;j < length;j += 3)
cin >> maze[i][j];
用字符’0‘来表示空格。
此外在遍历的过程中,使用队列来保存目前所处的空白的横纵坐标,但是若要纵向移动时,会出现一个小问题,那就是如果上下两个方格的斜杠向同一方向倾斜时,从上面空格到下面空格的纵坐标会出现2的差值,如下所示:
如图标出了两个三角的纵坐标,他们的纵坐标相差二,但是实际上在纵向移动时他们应该是挨在一起的,因此在遍历时,因判断是否存在这样上下两个方格中的斜杠朝同一方向倾斜,在入队时应考虑将纵坐标加2或减2。
完整代码如下:
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
char** maze;
int width,length;
struct route {
int i, j, MAX;
};
int M=0;
queue<route>qu;
void cal(int i, int j) {//开始判断当前位置是否有合适的空格可以入队,每次入完队会将空格打上标记,表示已经访问过了
if (j + 1 < length) {//判断当前空格右边是否有斜杠
if (maze[i][j + 1] == '\\') {//出现左斜杠,说明仅能向下或向左移动
if (j - 1 >= 0 && maze[i][j - 1] != '1') {//向左移动
qu.push(route{ i,j - 1,qu.back().MAX + 1 });
maze[i][j - 1] = '1';
}
else if (i + 1 < width) {
if (maze[i + 1][j + 2] == '0' && maze[i + 1][j + 1] == '\\') {//向下移动时,判断是否存在向同一方向倾斜的斜杠
qu.push(route{ i + 1,j + 2 ,qu.back().MAX + 1 });
maze[i + 1][j + 2] = '1';
}
else if(maze[i + 1][j] != '1') {//若没有,则直接移动,将下面的空格装入队列
qu.push(route{ i + 1,j ,qu.back().MAX + 1 });
maze[i + 1][j] = '1';
}
}
}
else if (maze[i][j + 1] == '/') {//若右边是右斜杠,原理同上
if (j - 1 >= 0 && maze[i][j - 1] != '1') {
qu.push(route{ i,j - 1,qu.back().MAX + 1 });
maze[i][j - 1] = '1';
}
else if (i - 1 >= 0) {
if (maze[i - 1][j + 2] == '0' && maze[i - 1][j + 1] == '/') {
qu.push(route{ i - 1,j + 2 ,qu.back().MAX + 1 });
maze[i - 1][j + 2] = '1';
}
else if( maze[i - 1][j] != '1') {
qu.push(route{ i - 1,j ,qu.back().MAX + 1 });
maze[i - 1][j] = '1';
}
}
}
}
if (j - 1 >= 0) {//判断左边
if (maze[i][j - 1] == '\\') {
if (j + 1 < length && maze[i][j + 1] != '1') {
qu.push(route{ i,j + 1,qu.back().MAX + 1 });
maze[i][j + 1] = '1';
}
else if (i - 1 >= 0) {
if (maze[i - 1][j - 2] == '0' && maze[i - 1][j - 1] == '\\') {
qu.push(route{ i - 1,j - 2,qu.back().MAX + 1 });
maze[i - 1][j - 2] = '1';
}
else if (maze[i - 1][j] != '1') {
qu.push(route{ i - 1,j,qu.back().MAX + 1 });
maze[i - 1][j] = '1';
}
}
}
else if (maze[i][j - 1] == '/') {
if (j + 1 < length && maze[i][j + 1] != '1') {
qu.push(route{ i,j + 1,qu.back().MAX + 1 });
maze[i][j + 1] = '1';
}
else if (i + 1 < width) {
if ( maze[i + 1][j - 2] == '0' && maze[i + 1][j - 1] == '/') {
qu.push(route{ i + 1,j - 2 ,qu.back().MAX + 1 });
maze[i + 1][j-2] = '1';
}
else if(maze[i + 1][j] != '1') {
qu.push(route{ i + 1,j ,qu.back().MAX + 1 });
maze[i + 1][j] = '1';
}
}
}
}
}
void maxRound() {
int i, j,k;
for (i = 0;i < width;++i) {//遍历迷宫中所有元素
for (j = 0;j < length;++j)
switch (maze[i][j]) {
case '0'://发现空格
qu.push(route{ i,j,0 });
maze[i][j] = '1';
k = 0;
while (!qu.empty()) {//当队列为空,跳出循环
cal(qu.front().i, qu.front().j);
if (qu.back().i == i && qu.back().j == j) {//当发现访问的空格是起点,比较回路大小
M = M < qu.back().MAX ? qu.back().MAX : M;
while (qu.size()>0){qu.pop();}
break;
}
qu.pop();
++k;
if (k == 2)maze[i][j] = '0';//当k==2,时,将起点设置为未被访问,目的是在之后的遍历中可以访问到起点,方便比较
}
break;
case '\\'://发现是其余字符时,直接跳过
case '/':
case '1':break;
default:
break;
}
}
}
int main() {
int i, j;
cin >> width >> length;
length *= 3;
maze = new char* [width];
for (i = 0;i < width;++i) {
maze[i] = new char[length];
memset(maze[i], '0', sizeof(char) * length);
}
for (i = 0;i < width;++i)
for (j = 1;j < length;j += 3)
cin >> maze[i][j];
maxRound();
cout << M;
for (i = 0;i < width;++i)
delete[]maze[i];
delete[]maze;
return 0;
}
最终输出的M即最大环路三角数。