在开始刷题这一个月里, 迄今为止耗时最久的一题!
题目虽然很长, 但是题意不难理解, 这个迷宫是一个定向迷宫, 每一点都有自己的规则
走每一步都要受到这个点的规则限制来选择下一点,
就这样, 给出起点和终点, 要找到一条从起点到达终点的路径
一开始没看到要找尽量短的路, 就直接用DFS写, 那叫一个easy
然而WA了3次, 是不是看漏条件了, 于是回头看题目, 才看到那句 "This route should not be longer than necessary, of course."
哭了, 再写一遍BFS
然而写到一半问题来了, 怎么保存路径呢?
一半的BFS标记一下父节点然后递归回去输出就行了, 但是这里的点是可重复的, 怎么办?
其实一样的, 虽然点可以走多次, 但是从某一个方向经过一个点只能有一次, 虽然麻烦了点, 那就用一个三维数组标记呗
现在回想一下好像难点也不多, 但是写的时候思路真的太乱了, 到最后又设了一个struct(一开始以为思路会很清晰就懒得设了)
其实主要是卡在BFS怎么保存路径上, 一开始以为有什么奇淫技巧, 其实不然, 存储父节点这个方式对再复杂的BFS也都是比较通用的
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <list>
#include <cassert>
#include <iomanip>
using namespace std;
const int MAXN = 32;
const int BASE = 10;
typedef long long LL;
/*
uva 816
关键 : 1.选择怎样的数据结构存储一个结点及其方向信息 :
1)简单的struct可用三维数组替代
2)to的方向有多种可能, 多项选择, 用位标来标记
2.【重点】图中可能有回路, 要标记
3. 找最短路径不能用DFS(适合寻找所有方案) , 必须用BFS(队列,适合寻找最佳方案)
4.可以用STL的pair来存储xy坐标
5.考虑到起点可能和终点一样
6. 【重点】 BFS的路径记录方法:用二维数组, 每个元素的值为在该点处下一点所在方向
*/
const int Move[4][2] = { {-1,0},{0,1},{1,0},{0,-1}, }; // 上右下左
typedef enum{
North = 0,
East = 1,
South = 2,
West = 3, // 这样取值可以使得 (Dir+2) % 4 为相反方向
Forward = 1, // 最右边的一位为1表示可以forward
Right = 1<<1, // 右数两位为1表示可以left
Left = 1<<2,
} Dir;
typedef pair<int,int> Pair;
int Map[MAXN][MAXN][4]; // Map[1][1][1]的值以二进制位标记录 从East经过该点要去的方向
map<char,int> dirMap;
bool mark[MAXN][MAXN][4]; // 标记一种状态是否出现过
int enX,enY,goalX,goalY;
bool isEnd;
vector<Pair> path;
void DFS(Pair pos,int to){ // DFS无法用于找最短路径
int dir;
if( pos.first==goalX && pos.second==goalY ){
// 找到路径, 输出path中的所有节点
path.push_back(pos);
cout << " ";
for(int i=1; i<=path.size(); i++){
printf(" (%d,%d)",path[i-1].first,path[i-1].second);
if( i%10==0 && i<path.size() ){
cout << endl;
if( i<path.size() ) cout << " ";
}
}
isEnd = true;
}else if( mark[pos.first][pos.second][to] ){
return ;
}else if( !isEnd ){
mark[pos.first][pos.second][to] = true;
int flag = Map[pos.first][pos.second][to];
path.push_back(pos);
for(int i=0; i<3; i++){
if( flag&(1<<i) ){
dir = (to+(1<<i)-1)%4;
DFS(Pair(pos.first+Move[dir][0],pos.second+Move[dir][1]),dir);
}
}
path.pop_back();
}
}
struct Point{
int x;
int y;
int dir;
Point(){ }
Point(int _x,int _y,int _dir):x(_x),y(_y),dir(_dir) { };
};
template <typename T>
T Pop(queue<T> &q){ // 注意这里 函数之间传递需要被改变的容器必须用引用,
// 因为传递后的只是原容器的复制品, 在函数内部改变不影响容器
T res = q.front();
q.pop();
return res;
}
void BFS(Pair start,int sDir){ // start是第二个点
Point record[MAXN][MAXN][4];
queue<Point> q;
Point pos(start.first,start.second,sDir); // dir标记是从哪个方向来的
q.push(pos);
memset(record,0,sizeof(record));
record[pos.x][pos.y][pos.dir] = Point(0,0,0);
while( !q.empty() && !isEnd ){
pos = Pop(q);
// cout << pos.x << " " << pos.y << " " << pos.dir << endl;
if( pos.x==goalX && pos.y==goalY ){
isEnd = true;
vector<Point> ans;
while( pos.x!=0 || pos.y!=0 ){ // 不能用起点作为结束标志, 因为起点有可能也是中间一个点
// printf("(%d,%d,%d)",pos.x,pos.y,pos.dir);
ans.push_back(pos);
pos = record[pos.x][pos.y][pos.dir];
}
ans.push_back(Point(enX,enY,0));
cout << " ";
for(int i=ans.size()-1, cnt=1; i>=0 ;cnt++,i--){
printf(" (%d,%d)",ans[i].x,ans[i].y);
if( cnt%10==0 && i>0){
cout << endl << " ";
}
}
}else if( !mark[pos.x][pos.y][pos.dir] ){ // 进入这里才算走进一个点
mark[pos.x][pos.y][pos.dir] = true;
int flag = Map[pos.x][pos.y][pos.dir];
for(int i=0; i<3; i++){
if( flag&(1<<i) ){
int dir = (pos.dir+(1<<i)-1)%4; // 是否要判断坐标 是否合法?
if( mark[pos.x+Move[dir][0]][pos.y+Move[dir][1]][dir] ) continue;
q.push(Point(pos.x+Move[dir][0],pos.y+Move[dir][1],dir));
record[pos.x+Move[dir][0]][pos.y+Move[dir][1]][dir] = pos;
}
}
}
}
}
void initial(){
dirMap['N'] = North;
dirMap['E'] = East;
dirMap['S'] = South;
dirMap['W'] = West;
dirMap['F'] = Forward;
dirMap['L'] = Left;
dirMap['R'] = Right;
}
int main(){
// freopen("input2.txt","r",stdin);
// freopen("output.txt","w",stdout)
string name;
int x,y,dir;
char to,ch = 0;
initial();
while( getline(cin,name) ){
if( name.length()==0 ){
continue;
}else if( name=="END" ){
break;
}
cin >> enX >> enY >> to >> goalX >> goalY;
dir = -1;
isEnd = false;
path.clear();
memset(Map,0,sizeof(Map));
memset(mark,0,sizeof(mark));
while( (cin >> x) && x>0 ){
cin >> y;
while( (ch=getchar())!='*' ){
if( ch=='N' || ch=='E' || ch=='S' || ch=='W' ){
dir = dirMap[ch];
}else if( ch=='F' || ch=='L' || ch=='R' ){
if( dir!=-1 ) Map[x][y][dir] |= dirMap[ch];
// printf("Map[%d][%d][%d] = %d\n",x,y,dir,Map[x][y][dir]);
}
}
while( getchar()!='\n' ) ;
}
cout << name << endl;
dir = dirMap[to];
path.push_back(Pair(enX,enY));
// DFS(Pair(enX+Move[dir][0],enY+Move[dir][1]),dir);
BFS(Pair(enX+Move[dir][0],enY+Move[dir][1]),dir);
if( !isEnd ) {
cout << " No Solution Possible" ;
}
cout << endl;
}
return 0;
}