算法思路:BFS。
这是一道用BFS求最短路的题,整个代码框架参考自“刘汝佳的《算法竞赛》”,包括bfs()函数和print_path()函数。
关键点:
1.因为存在具有Hp为n的monster存在,如果按照常规的方法直接压入队列,得到的并不会是题目要求的“最短路”,因此必须在处理的过程中想办法当遇到monster的时候在原地等待,等待时间到了之后“自动”的和其他的结点一起继续向“外部”扩散。
处理方法:如果当前位置是monster,那么我就削减他一个HP,再压入队尾,直到HP为0时,继续向四周扩散。
2.路径的打印比较繁琐,需要注意很多的细节,小心点就好了。
另外听人说算法导论上的记忆化BFS可以很好地解决打印问题,具体的操作是建立一个记忆表。
弄了一个晚上做这道题,但也是第一道BFS,还是挺开心的!刚开始因为没有注意到“因为monster的存在而不能用常规的BFS做”,但是Sample竟然过了,导致很久没看出来错误的地方,可见这个Sample还是挺阴险的^_^,不过还是要努力地“仔细看题 + 同时训练思维的清晰性”。后来改正了bfs()函数,0msAC,代码如下:
//模板开始
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <set>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include<iomanip>
#include<string.h>
#define SZ(x) (int(x.size()))
using namespace std;
int toInt(string s){
istringstream sin(s);
int t;
sin>>t;
return t;
}
template<class T> string toString(T x){
ostringstream sout;
sout<<x;
return sout.str();
}
typedef long long int64;
int64 toInt64(string s){
istringstream sin(s);
int64 t;
sin>>t;
return t;
}
template<class T> T gcd(T a, T b){
if(a<0)
return gcd(-a, b);
if(b<0)
return gcd(a, -b);
return (b == 0)? a : gcd(b, a % b);
}
//模板结束(通用部分)
#define ifs cin
#define MAX_LEN 105
char maze[MAX_LEN][MAX_LEN];
int n, m;
int dx[4] = {0, 0, -1, 1}; //分别对应四个方向
int dy[4] = {1, -1, 0, 0};
int vis[MAX_LEN][MAX_LEN]; //结点状态,唯一要注意的是当遇到monster时状态设为-1,表示锁定,结点原地等待,同时不能被相邻的结点访问到,当时间到了之后,需要解锁即状态设为1。
int fa[MAX_LEN][MAX_LEN]; //存储当前结点的下一个结点的“编号”。
//hp和hp1的初始值是一样的,都是monster的HP值,没有monster则为0。
int hp[MAX_LEN][MAX_LEN]; //始终不变,print_path函数中用到
int hp1[MAX_LEN][MAX_LEN]; //动态改变, bfs函数中模拟“遇到monster原地不动”用到
int q[100000];
int front;
int rear; //指向队列最后一个元素的下一个位置
void bfs(int x, int y)
{
front = 0;
rear = 0;
int d, u;
u = x * m + y;
vis[x][y] = 1;
fa[x][y] = u;
q[rear++] = u;
while(front < rear)
{
u = q[front++];
x = u / m;
y = u % m;
if(vis[x][y] == -1) //判断当前结点是否处于锁定状态
{
if(hp1[x][y] != 0)
{
int v = x * m + y; //让该结点原地不动,等待
q[rear++] = v;
hp1[x][y]--;
continue; //继续下一次循环
}
else
{
vis[x][y] = 1; //继续下面执行
}
}
for(d = 0; d < 4; d++)
{
int nx = x + dx[d];
int ny = y + dy[d];
if(vis[x][y] != -1 && nx >= 0 && nx < n && ny >= 0 && ny < m
//vis[x][y] == -1表示锁定状态
&& maze[nx][ny] == '.' && !vis[nx][ny]) //下一个结点是‘.'。
{
int v = nx * m + ny;
q[rear++] = v;
vis[nx][ny] = 1;
fa[nx][ny] = u;
}
else if(vis[x][y] != -1 && nx >= 0 && nx < n && ny >= 0 && ny < m
&& maze[nx][ny] != 'X' && !vis[nx][ny] ) //下一个结点是monster。
{
int v = nx * m + ny;
q[rear++] = v;
fa[nx][ny] = u;
vis[nx][ny] = -1; //设置锁定
}
}
}
}
int xpath[MAX_LEN * MAX_LEN]; //print_path函数用到,用于记录路径上的结点的x坐标。
int ypath[MAX_LEN * MAX_LEN]; //print_path函数用到,用于记录路径上的结点的y坐标。
int c;
void print_path(int x_aim, int y_aim) //打印路径
{
if(!vis[x_aim][y_aim])
{
cout<<"God please help our poor hero."<<endl<<"FINISH"<<endl;
return;
}
int fight = 0;
int c = 0;
for( ; ; )
{
int fx = fa[x_aim][y_aim] / m;
int fy = fa[x_aim][y_aim] % m;
if(fx == x_aim && fy == y_aim)
{
break;
}
xpath[c] = x_aim;
ypath[c] = y_aim;
if(hp[x_aim][y_aim] != 0)
{
fight += hp[x_aim][y_aim];
}
c++;
x_aim = fx;
y_aim = fy;
}
xpath[c] = x_aim; //补上最后一次执行的值。循环中并没有执行这一次!
ypath[c] = y_aim; //这样的话c的值刚刚好等于“步数 = 路径上点的个数 - 1”。
if(hp[x_aim][y_aim] != 0) //虽然题目说起点没有monster,但是判断一下也可以,防止测试数据出错
{
fight += hp[x_aim][y_aim];
}
int time = 1;
int temp;
cout<<"It takes "<<c + fight
<<" seconds to reach the target position, let me show you the way."<<endl;
for(temp = c; temp >= 1; temp--)
{
if(hp[xpath[temp]][ypath[temp]] == 0)
{
cout<<time++<<"s:("<<xpath[temp]<<","
<<ypath[temp]<<")->("<<xpath[temp - 1]
<<","<<ypath[temp - 1]<<")"<<endl;
}
else
{
while(hp[xpath[temp]][ypath[temp]] != 0)
{
cout<<time++<<"s:FIGHT AT ("<<xpath[temp]<<","
<<ypath[temp]<<")"<<endl;
hp[xpath[temp]][ypath[temp]]--;
}
cout<<time++<<"s:("<<xpath[temp]<<","
<<ypath[temp]<<")->("<<xpath[temp - 1]
<<","<<ypath[temp - 1]<<")"<<endl;
}
}
if(hp[xpath[temp]][ypath[temp]] != 0) //虽然起点不会是monster,但是终点有可能是!
{
while(hp[xpath[temp]][ypath[temp]] != 0)
{
cout<<time++<<"s:FIGHT AT ("<<xpath[temp]<<","
<<ypath[temp]<<")"<<endl;
hp[xpath[temp]][ypath[temp]]--;
}
}
cout<<"FINISH"<<endl;
}
//【练习06】BFS 1002 Ignatius and the Princess I
int main()
{
//ifstream ifs("shuju.txt", ios::in);
while(ifs>>n>>m)
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
vis[i][j] = 0;
ifs>>maze[i][j];
if(maze[i][j] == '.')
{
hp[i][j] = 0;
hp1[i][j] = 0;
}
else if(maze[i][j] != 'X')
{
hp[i][j] = int(maze[i][j] - '0');
hp1[i][j] = int(maze[i][j] - '0');
}
}
}
bfs(0, 0);
print_path(n - 1, m - 1);
}
return 0;
}