CF1063B Labyrinth
题目描述
You are playing conputer game. One of its levels puts you in a maze consisting of n lines, each of which contains m cells. Each cell eigher is free of is occupied by an obstacle. The starting cell in the row r and column c. In one step you can move one square up,left ,down or right, if the target cell is not occupied by an obstacle . You can't move beyond the boundaries of the labyrinth.
Unfortunately,your keyboard is about to break,so you can move left no more than x times and move right no more than y times. There are no restrictions on the number of moves up down since the keys used to move up and down are in perfect condition.
Now you would like to determine for each cell whether there a sequence of moves thar will put you from starting cell to this particular one. How many cells of the board have this property?
输入格式
The first line contains two integers n,m(\(1\leq n,m\leq 2000\))-the number of rows and the nmber columns in the labyrinth respectively.
The second line contains two integers r,c (\(1\leq r\leq n,1\leq c \leq m\))--index of the row and index of hte colunm that define the starting cell.
The third line contains two integers x,y(\(0\leq x,y\leq 10^{9}\))--the maximum allowed number of movements to the left and to the right respectively.
The next n lines describe the labyrinth.Each of them has length of m and consists only of symbols '.' and '*' . The j-th character of the i-th line corresponds to the cell of labyrinth at row i and column j.Symbol '.' denotes the free cell, while symbol '$ * $' denotes the cell with an obstacle.
It is guaranteed, that the starting cell contains no obstacles.
输出格式
Print exactly one integer--the number of cells in the labyrinth, which are reachable from starting cell, including the starting cell itself.
输入输出样例
输入 #1
4 5
3 2
1 2
.....
.***.
...**
*....
输出 #1
10
输入 #2
4 4
2 2
0 1
....
..*.
....
....
输出 #2
7
说明/提示
Cells,reachable in the corresponding example, are marked with '+'
First example:
+++..
+***.
+++**
*+++.
Second example:
.++.
.+*.
.++.
.++.
题解
#include<iostream>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
int r,c;
int x,y;
char map[2005][2005];//存储地图
int Right[2005][2005];//维护向右走的步数
int Left[2005][2005];//维护向左走的步数
//定义一个节点类
class node{
public:
int x;//当前格子的x坐标
int y;//当前格子的y坐标
int r;//当前格子向右走的步数
int l;//当前格子向左走的步数
node(int x=0,int y=0,int r=0,int l=0){
this->x=x;
this->y=y;
this->r=r;
this->l=l;
}
};
queue<node> que;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
int answer;
void bfs(){
//首先将起点入队
que.push(node(r,c,0,0));
//在起点时,向左走的步数和向右走的步数都是0
Right[r][c]=0;
Left[r][c]=0;
//起点算能到达的第一个格子
answer++;
//开始BFS
while(!que.empty()){
//从队列中取出一个节点
node now=que.front();
que.pop();
//向四个方向扩展
for(int i=0;i<4;i++){
int dx=dir[i][0]+now.x;
int dy=dir[i][1]+now.y;
//如果从now节点向右走了,mr就加1
//如果从now节点向左走了,ml就加1
int ml=0;
int mr=0;
if(dir[i][1]==1){
mr++;
}
if(dir[i][1]==-1){
ml++;
}
//判断总的向右走的步数是否超过限制
if(mr+now.r>y){
continue;
}
//判断总的向左走的步数是否超过限制
if(ml+now.l>x){
continue;
}
//如果下一个格子超出了地图边界或者为障碍物,就跳过
if(dx<1||dx>n||dy<1||dy>m||map[dx][dy]=='*'){
continue;
}
//如果下一个格子的向左向右的步数更优或者相等的话,就跳过
if(Right[dx][dy]<=now.r+mr&&Left[dx][dy]<=now.l+ml){
continue;
}
//如果下一个格子的向左向右的步数更差的话
if(Right[dx][dy]>now.r+mr&&Left[dx][dy]>now.l+ml){
//并且这个格子没有到达过的话,就把答案加1
if(Right[dx][dy]==inf&&Left[dx][dy]==inf){
answer++;
}
//更新此时的向左向右数组
Right[dx][dy]=now.r+mr;
Left[dx][dy]=now.l+ml;
}
//将这个节点入队
que.push(node(dx,dy,Right[dx][dy],Left[dx][dy]));
}
}
}
int main(){
scanf("%d%d",&n,&m);
scanf("%d%d",&r,&c);
scanf("%d%d",&x,&y);
getchar();
//初始化向右向左数组为无穷
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
Right[i][j]=inf;
Left[i][j]=inf;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%c",&map[i][j]);
}
getchar();
}
bfs();
printf("%d",answer);
return 0;
}
这道题核心就是宽搜,但是和普通的宽搜不一样,它这个有向左向右的限制,所以我们在每个格子的节点中把向左向右的次数也加进去,如果向左向右的次数超过了限制,就不去扩展下一个格子了,正常想当然都会这么想,但是实际上这题远不止这些东西。
我们还要注意一个问题,就是路径的重复问题,在正常的BFS中,每把一个节点放入队列,我们就会标记这个节点,然后下次就不会再访问到这个节点了。但是在这道题中,我们不能这样做,有些访问过的点我们必须还要重复访问,为什么?比如说,我们现在有一个地图,规定向右只能走2格,向左只能走1格,你用路径A走到了S点,在这次行走过程中,你向右走了2步,向左走了1步,然后才到的S点。但是你如果用路径B走到S点的话,你只需要向右走1步,不用向左走,那显然路径B是要比路径A要优秀的,因为当你用路径A走到S点时,此时你既不能向右走了,也不能向左走了。但如果你用路径B走到S点的话,你既可以向右走一步,还可以向左走一步。这样你就可以探索到更多的格子,这才是最优的。
在正常的BFS中,走过的路是不能再走的,如果你此时恰巧先走的路径A,然后你把S点标记上了,这样的话,路径B也走不到S点了,所以那些只能由S点向外探索的点就探索不到了,所以就会出现错误结果。所以我们不能像传统BFS那样简单的标记,我们需要更复杂的标记系统来说明我们到底要不要探索这个点,而不是简单的只是经历过这个点就不探索了。从这道题的性质上来说,我们必须要有重复探索一个点的过程。
那么怎么办呢?我的做法是用两个数组来维护地图上从出发点到每个点最少的向左向右走的格数,注意,这个向左向右数组是配套的,也就是针对一条路径来说的,他描述的是这条路径的好坏,如果一条路径向左走的次数优于目前这个但是向右走的不及目前这个,这样的话是不更新数组的(但是这样写的话也可以AC,那好像这就是无关紧要的事情吧),只有向左走和向右走的次数全都优于目前这个的话,才会更新数组
有了这个数组后,我们就可以进行搜索了,首先我们把这两个数组初始化成无穷大(其实只是一个很大的数而已,哪有无穷大),然后就开始搜索,这两个数组会记录一条最优的路径到达这个点时向左走了多少步,向右走了多少步。如果有另一条路径走到这个点时向左走的步数小于这个目前最优的,向右走的步数也小于这个目前最优的,那么我们就更新这个向左向右数组。如果另一条路径走到这个点时向左走的步数大于目前这个最优的,向右走的步数也大于这个目前最优的,那么我们就略过这条路径,不再往下探索,因为这条最优的路径肯定会比这条路径探索到更多的点,所以这条路径就没有探索的价值了。如果另一条路径走到这个点时向左走的步数大于目前这个最优的,向右走的步数小于目前这个最优的,我们不更新数组,但是我们会继续向下探索,因为只要有一个优于目前最优的就可能会探索出新的点来。如果另一条路径走到这个点时向左走的步数小于目前这个最优的,向右走的步数大于目前这个最优的,我们也不会更新数组,但会继续向下探索。如果探索途中遇到了向左向右数组同时为无穷大的场景,那么我们就把答案加1,因为还是无穷大的话,就代表我们没来过这个地方。像这样标记,我们就能最大程度的找到所有可以到达的点。