题目来源: RevolvingDoors
感觉这道题比前两道应用BFS算法的题目要难一些,不像之前的那么直接,如果这道题目要求的是最短的频数的话,那跟前两道就差不多了,但这里求的是最少旋转门的次数,这就有点复杂了,我的思路如下:
1. 判断当前位置是否是终点,不是则搜索由当前位置可以到达的旋转门,并将旋转之后的结果状态存入LD链表尾。
2. 从LD链表中取出第一个元素作为当前状态,重复步骤1.
遇到的问题:BFS算法需要设置一个访问状态标志,要不然会发生重复搜索同一个状态的情况,开始的时候我只是设置了一个visited数组确定搜索可到达的旋转门的过程不会出现重复搜索的过程,结果出现了大问题,因为搜索过程中,进入LD中的元素会有重复的,所以会进行大量的重复搜索,时间复杂度大大增加,而且遇到某些地图还会出现无限循环的情况,如地图示例1,而地图示例2直接就是算半天算不出来,后来我增加了一个 isVisited函数确保进入LD的元素跟以前进入过的元素没有重复,问题才解决了。
还有一个要注意的问题就是地图示例的情况不是旋转6次,而是7次,最后一次也要先旋转门才能到达终点,开始的程序少算了这一步。
代码如下:
欢迎大家提出改正意见或者在回复里面show上自己的代码
/**
* TopCoder:RevolvingDoors
* (http://community.topcoder.com/stat?c=problem_statement&pm=3064&rd=5869)
* Author: xuzhezhao
* E-mail: zhezhaoxu@gmail.com
* Blog: http://blog.csdn.net/xuzhezhaozhao/
* Date: 2013/6/7
*/
#include <iostream>
#include <string>
#include <list>
using namespace std;
#define MAX 50 /* 地图无数限制数 */
#define DOORS_MAX 10 /* 门数限制 */
class RevolvingDoors
{
public:
int turns(string map[]);
};
enum Door_State {H, V}; /* 门的状态,H:水平 V:竖直 */
typedef struct CurrentPos{
int row; /* 当前所处行 */
int col; /* 当前所处列 */
Door_State door[MAX][MAX]; /* 门状态数组 */
int door_pos[DOORS_MAX]; /* 门位置信息 */
int doors_num; /* 门数量 */
int turn_doors; /* trun的门数量 */
}CurrentPos;
static int map_rows = 0; /* 地图行数 */
static int map_cols = 0; /* 地图列数 */
static int end_row; /* 终点所在行数 */
static int end_col; /* 终点所在列数 */
static int visited[MAX][MAX];
void init(string map[], CurrentPos &curpos);
bool isEnd(CurrentPos curpos);
bool isVisited(list <CurrentPos> LV, CurrentPos curpos);
int main()
{
RevolvingDoors revolvingdoors;
/* 地图,以 "" 结尾 */
string map[] =
{
"#############",
"# #|##|# #",
"# O O #",
"# E || || S #",
"# O O #",
"# #|##|# #",
"#############",
"" };
cout << revolvingdoors.turns(map) << endl;
return 0;
}
int RevolvingDoors::turns(string map[])
{
list <CurrentPos> LC; /* LC: 内层BFS状态 */
list <CurrentPos> LD; /* LD: 外层BFS,turn 操作之后的状态 */
list <CurrentPos> LV; /* LV:外层BFS访问状态标志,类似visited数组,保存turn操作之后的状态,以避免重复 */
CurrentPos curpos, nextpos;
int row, col;
init(map, curpos);
LD.push_back(curpos);
/* 外层BFS */
while (!LD.empty()) {
curpos = LD.front();
LD.pop_front();
/* 初始化visited数组 */
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
visited[i][j] = false;
}
}
LC.push_back(curpos);
/* 内层BFS */
while (!LC.empty()) {
curpos = LC.front();
LC.pop_front();
row = curpos.row;
col = curpos.col;
visited[row][col] = true;
if (isEnd(curpos)) {
return curpos.turn_doors;
}
/* 可以向上下左右四个方向推进状态 */
/* 向上走 */
nextpos = curpos;
if (row - 1 >= 0 && !visited[row-1][col]) {
nextpos.row = row - 1;
if (' ' == map[row-1][col]) {
LC.push_back(nextpos);
} else if ('*' == map[row-1][col]) { /* 进入门的四周区域 */
/* 门的位置有三种情况 */
if (row - 2 >= 0 && 'O' == map[row-2][col] && H == curpos.door[row-2][col] ) {
LC.push_back(nextpos);
} else if (col + 1 <= map_cols - 1 &&
'O' == map[row-1][col+1]) {
if (V == curpos.door[row-1][col+1]) {
LC.push_back(nextpos);
} else { /* turn door */
nextpos.door[row-1][col+1] = V;
++nextpos.turn_doors;
visited[row-1][col] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
} else if (col - 1 >= 0 && 'O' == map[row-1][col-1]) {
if (V == curpos.door[row-1][col-1]) {
LC.push_back(nextpos);
} else {
nextpos.door[row-1][col-1] = V;
++nextpos.turn_doors;
visited[row-1][col] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
}
}
}
/* 向下走 */
nextpos = curpos;
if (row + 1 <= map_rows - 1 && !visited[row+1][col]) {
nextpos.row = row + 1;
if (' ' == map[row+1][col]) {
LC.push_back(nextpos);
} else if ('*' == map[row+1][col]) {
if (row + 2 <= map_rows - 1 && 'O' == map[row+2][col] &&
H == curpos.door[row+2][col] ) {
LC.push_back(nextpos);
} else if (col + 1 <= map_cols - 1 && 'O' == map[row+1][col+1]) {
if (V == curpos.door[row+1][col+1]) {
LC.push_back(nextpos);
} else { /* turn door */
nextpos.door[row+1][col+1] = V;
++nextpos.turn_doors;
visited[row+1][col] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
} else if (col - 1 >= 0 &&
'O' == map[row+1][col-1]) {
if (V == curpos.door[row+1][col-1]) {
LC.push_back(nextpos);
} else {
nextpos.door[row+1][col-1] = V;
++nextpos.turn_doors;
visited[row+1][col] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
}
}
}
/* 向左走 */
nextpos = curpos;
if (col - 1 >= 0 && !visited[row][col-1]) {
nextpos.col = col - 1;
if (' ' == map[row][col-1]) {
LC.push_back(nextpos);
} else if ('*' == map[row][col-1]) {
if (col - 2 >= 0 && 'O' == map[row][col-2] &&
V == curpos.door[row][col-2] ) {
LC.push_back(nextpos);
} else if (row - 1 >= 0 &&
'O' == map[row-1][col-1]) {
if (H == curpos.door[row-1][col-1]) {
LC.push_back(nextpos);
} else { /* turn door */
nextpos.door[row-1][col-1] = H;
++nextpos.turn_doors;
visited[row][col-1] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
} else if (row + 1 <= map_rows - 1 && 'O' == map[row+1][col-1]) {
if (H == curpos.door[row+1][col-1]) {
LC.push_back(nextpos);
} else {
nextpos.door[row+1][col-1] = H;
++nextpos.turn_doors;
visited[row][col-1] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
}
}
}
/* 向右走 */
nextpos = curpos;
if (col + 1 <= map_cols - 1 && !visited[row][col+1]) {
nextpos.col = col + 1;
if (' ' == map[row][col+1]) {
LC.push_back(nextpos);
} else if ('*' == map[row][col+1]) {
if (col + 2 <= map_cols - 1 && 'O' == map[row][col+2] &&
V == curpos.door[row][col+2] ) {
LC.push_back(nextpos);
} else if (row + 1 <= map_rows - 1 &&
'O' == map[row+1][col+1]) {
if (H == curpos.door[row+1][col+1]) {
LC.push_back(nextpos);
} else { /* turn door */
nextpos.door[row+1][col+1] = H;
++nextpos.turn_doors;
visited[row][col+1] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
} else if (row - 1 >= 0 &&
'O' == map[row-1][col+1]) {
if (H == curpos.door[row-1][col+1]) {
LC.push_back(nextpos);
} else {
nextpos.door[row-1][col+1] = H;
++nextpos.turn_doors;
visited[row][col+1] = true;
if (!isVisited(LV, nextpos)){
LV.push_back(nextpos);
LD.push_back(nextpos);
}
}
}
}
}
}
}
return -1;
}
/**
* 初始操作,获得地图的基本信息,行,列,门状态。并将门四周的4个点置为 '*' 字符,将 S, E 置为' '(空格)。
*/
void init(string map[], CurrentPos &curpos)
{
int i, j;
curpos.turn_doors = 0;
while (map[map_rows] != "") {
++map_rows;
}
if (map_rows > 0) {
map_cols = map[0].length();
}
for (i = 0; i < map_rows; i ++) {
for (j = 0; j < map_cols; j++) {
if ('S' == map[i][j]) {
curpos.row = i;
curpos.col = j;
map[i][j] = ' ';
}
if ('E' == map[i][j]) {
end_row = i;
end_col = j;
map[i][j] = ' ';
}
}
}
curpos.doors_num = 0;
for (i = 0; i < map_rows; i ++) {
for (j = 0; j < map_cols; j++) {
if ('O' == map[i][j]) {
curpos.door_pos[curpos.doors_num] = i * map_cols + j;
++curpos.doors_num;
if ('|' == map[i+1][j]) {
curpos.door[i][j] = V;
} else {
curpos.door[i][j] = H;
}
map[i+1][j] = map[i-1][j] =
map [i][j+1] = map[i][j-1] = '*';
}
}
}
}
/**
* 判断是否到达终点
*/
bool isEnd(CurrentPos curpos)
{
if (curpos.row == end_row && curpos.col == end_col) {
return true;
} else {
return false;
}
}
/**
* 外层BFS访问标志,若LV中含有curpos状态,返回true,否则返回false
*/
bool isVisited(list <CurrentPos> LV, CurrentPos curpos)
{
list<CurrentPos>::iterator it;
int door_row, door_col;
bool flag;
if (LV.empty()) {
return false;
}
for (it = LV.begin(); it != LV.end(); it++) {
if (it->row == curpos.row && it->col == curpos.col) {
flag = true;
for (int i = 0; i < curpos.doors_num; i++) {
door_row = curpos.door_pos[i] / map_cols;
door_col = curpos.door_pos[i] % map_cols;
if (it->door[door_row][door_col] != curpos.door[door_row][door_col]) {
flag = false;
break;
}
}
if (flag) {
return true;
}
}
}
return false;
}