题目链接
花了一早上研究前辈的代码,才搞懂这题目。经常,做一道题目,总是要先看题解,才能做,很多时候,甚至做的时候也是照搬前辈的代码。不知道对不对,迷茫过很久,但是想想,怎么说呢,就算是抄袭别人的代码,但是自己还是学习了很多。至少你懂得了这道题目的思路了。你知道每行代码他有他自己存在的意义,不是为了美观,不是冗余而是一个独一无二的作用。所以,可能这样的方法,前进的不是那么快,那么远。可是,如今这种情况下还是蛮适合的。
~~~~~貌似扯远了,说下这道题目吧。刚开始刷OJ不久,做过DFS深度优先搜索,今天终于让我做到广度优先搜素了。不过这道题目不像前面的DFS那么简单,他多了个怪兽这东西,而且那个输出着实坑爹。
所以,必须利用队列来实现输出。
思路倒是挺简单的。具体每一步在干嘛,我写的很清楚的在代码的注释里面了。——没有注释的代码就缺少可读性,没有可读性的代码就是垃圾。
#include <iostream> #include <memory.h> #include <queue> using namespace std; char maze[101][101]; //迷宫地图 bool visited[101][101]; //BFS记录是否访问过 int n,m; int stepx[4]={0,1,0,-1}; //stepx和stepy错开, int stepy[4]={-1,0,1,0}; //分别为下,左,上,右 int depth; //记录走过的路长,即到达终点的时间 struct point{ int x; int y; int hp; int step; //每个结点的路长 }; point path[102][102];//用来保存路长保存的是上一个到达此结点的值 queue <point> q;//广搜队列 bool isBound(point p) { if(p.x<0||p.x>=n||p.y<0||p.y>=m) { return false; } return true; } //输出函数,其中path[x][y]中保存的是上一个到达此结点的值 void outputPath(int x,int y,int hp) { if(path[x][y].x + path[x][y].y !=0) //从(0,0)开始 outputPath(path[x][y].x,path[x][y].y,path[x][y].hp); int px = path[x][y].x; int py = path[x][y].y; cout<< depth++<<"s:("<<px<<","<<py<<")->("<<x<<","<<y<<")"<<endl; for(int i=1;i<=hp;++i) //注意这里容易弄混,就是我们BFS里面是用maze数组的hp减少来实现的,然而我们弄了个path数组则避免hp的丢失 cout << depth++ << "s:FIGHT AT ("<< x << "," << y << ")" << endl; } ///广度优先搜索,查找最佳路径。这里这样处理:由于怪物的存在,破坏了广度优先搜索的结构,因此每次经过怪物时,将hp-1,再将值赋给 ///这一点,并将此点入队列,并不直接进行扩展,直到hp减为0后,将这一点设置为'.'即一般路径,进行扩展,这样能够保证同一层次找到的 ///第一个解即为最优解(因为这样处理后,所有扩展的节点代价都相等都为1) void BFS() { bool rescue = false; point curr,next; curr.x = curr.y = curr.step = curr.hp =0; //初始化起始位置 q.push(curr); visited[0][0] = true ; // (0,0)是入口所以一直是被访问过的 while(!q.empty()) { curr = q.front(); q.pop(); //如果到达终点 则可以进行输出 if(curr.x == n-1 && curr.y == m-1 && maze[curr.x][curr.y] == '.') //为什么不考虑最后有怪兽呢?可通过下面方法解决终点有怪兽情况 { rescue = true; cout << "It takes " << curr.step << " seconds to reach the target position, let me show you the way." << endl; depth = 1; outputPath(curr.x,curr.y,curr.hp); } //若碰到怪兽则将其HP--,并且将其加入队列中,这也可以解决最后有怪兽的问题。 if(maze[curr.x][curr.y] >= '1' && maze[curr.x][curr.y]<='9') { int tmp = maze[curr.x][curr.y] - '1'; if(tmp == 0) maze[curr.x][curr.y] = '.'; //如果hp减到0 则可以将其视为一个'.' else maze[curr.x][curr.y] = tmp + '0'; curr.step ++; //一滴一滴血的打 q.push(curr); } else //普通结点,进行扩展 { //分别朝着四个方向走 for(int i=0;i<4;i++) { next.x = curr.x + stepx[i]; next.y = curr.y + stepy[i]; next.step = curr.step + 1; //如果没访问过,不是墙壁,且不是边界则保存下来 if(!visited[next.x][next.y]&&isBound(next)&&maze[next.x][next.y]!='X') { visited[next.x][next.y] = true; path[next.x][next.y].x = curr.x; path[next.x][next.y].y = curr.y; path[next.x][next.y].hp = curr.hp; //更新路径的血量(这样处理为了通用性,将怪兽点与路径点用同样方式处理,对怪兽点的扩展按照上面if方法) if(maze[next.x][next.y] == '.') next.hp = 0; else next.hp = maze[next.x][next.y] - '0' ; //保存怪兽HP q.push(next); } } } } if(!rescue) cout << "God please help our poor hero." << endl; } int main() { while(cin>>n>>m) { //构造迷宫 并初始化 for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { cin>>maze[i][j]; visited[i][j] = false; } } memset(path,0,sizeof(path)); BFS(); cout << "FINISH" << endl; } return 0; }