CCF 201312-5 I’m stuck! 两次深搜解法

问题描述
  给定一个R行C列的地图,地图的每一个方格可能是'#', '+', '-', '|', '.', 'S', 'T'七个字符中的一个,分别表示如下意思:
  '#': 任何时候玩家都不能移动到此方格;
  '+': 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;
  '-': 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非'#'方格移动一格;
  '|': 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非'#'方格移动一格;
  '.': 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为'#',则玩家不能再移动;
  'S': 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;
  'T': 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格。
  此外,玩家不能移动出地图。
  请找出满足下面两个性质的方格个数:
  1. 玩家可以从初始位置移动到此方格;

  2. 玩家不可以从此方格移动到目标位置。

输入格式
  输入的第一行包括两个整数R 和C,分别表示地图的行和列数。(1 ≤ R, C ≤ 50)。

  接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个'S'和一个'T'。

输出格式

  如果玩家在初始位置就已经不能到达终点了,就输出“I'm stuck!”(不含双引号)。否则的话,输出满足性质的方格的个数。

样例输入

5 5
--+-+
..|#.
..|##
S-+-T

####.

样例输出

2

样例说明
  如果把满足性质的方格在地图上用'X'标记出来的话,地图如下所示:
  --+-+
  ..|#X
  ..|##
  S-+-T

  ####X


题目分析:

比较简单的一道图论题目,从起点出发的可达的点利用深搜或者广搜都可以做到,求哪些点可以到达终点时,网上多数做法是对矩阵中每一个点做一次深搜或广搜,但是这样效率很低。


本文在求哪些点可到终点时,反向思考从终点出发可以到达哪些点,修改部分规则,比如遇到'.',我们必须确保是从下方走上来的,才可以回去。遇到'-',认为必须从左右来,遇到'|',认为必须从上下来。


代码如下:


#include <iostream>
#include <memory.h>
using namespace std;

#define DIM	50

//正向深搜,从起点出发 
void ForwardDFS(char map[DIM][DIM],//输入矩阵 
				bool sign[DIM][DIM],//返回标记结果图,true表示可以到达这个点,false表示不能到达 
				int Row,int Col,//矩阵规模 
				int curR, int curC)//当前点 
{
	//若 当前点已走过 或 当前点是障碍物,停止递归 
	if (sign[curR][curC] || map[curR][curC] == '#')
	{
		return ;
	}
	//标记此点可达 
	sign[curR][curC] = true;
	//标记可以往哪个方向移动
	bool up,down,left,right;
	up = down = left = right = false; 
	if (map[curR][curC] == '.')//向下移动 
	{
		down = true;
	}
	else if (map[curR][curC] == '-')//左右移动 
	{
		left = right = true;
	}
	else if (map[curR][curC] == '|')//上下移动 
	{
		up = down = true;
	}
	else if (map[curR][curC] == '+' || map[curR][curC] == 'S' || map[curR][curC] == 'T')//上下左右移动 
	{
		up = down = left = right = true;
	}
	//上
	if (up && curR-1 >= 0)
	{
		ForwardDFS(map,sign,Row,Col,curR-1,curC);
	}
	//下 
	if (down && curR+1 < Row)
	{
		ForwardDFS(map,sign,Row,Col,curR+1,curC);
	}
	//左
	if (left && curC-1 >= 0)
	{
		ForwardDFS(map,sign,Row,Col,curR,curC-1);
	}
	//右
	if (right && curC+1 < Col)
	{
		ForwardDFS(map,sign,Row,Col,curR,curC+1);
	}
}

//从终点出发,反向DFS 
void ReserveDFS(char map[DIM][DIM],//输入矩阵 
				bool sign[DIM][DIM],//返回标记结果图,true表示可以到达这个点,false表示不能到达 
				int Row,int Col,//矩阵规模 
				int curR, int curC,//当前点 
				int preR, int preC)//上一个点 
{
	//若 当前点已走过 或 当前点是障碍物,停止递归 
	if (sign[curR][curC] || map[curR][curC] == '#')
	{
		return ;
	}
	//从下面来的,可以返回去,其他方向来的无法原路返回
	if (map[curR][curC] == '.' && preR == curR+1 && preC == curC)
	{	
		sign[curR][curC] = true;
	}
	//从左右来的可以返回去
	else if (map[curR][curC] == '-' && preR == curR)
	{	
		sign[curR][curC] = true;
	}
	//必须从上下来 
	else if (map[curR][curC] == '|' && preC == curC)
	{
		sign[curR][curC] = true;
	}
	//可随便返回 
	else if (map[curR][curC] == 'S' || map[curR][curC] == '+' || map[curR][curC] == 'T')
	{
		sign[curR][curC] = true;
	}
	//不可达,返回 
	if (sign[curR][curC] == false)
	{
		return ;
	}
	//上
	if (curR-1 >= 0)
	{
		ReserveDFS(map,sign,Row,Col,curR-1,curC,curR,curC);
	}
	//下
	if (curR+1 < Row)
	{
		ReserveDFS(map,sign,Row,Col,curR+1,curC,curR,curC);
	}
	//左
	if (curC-1 >= 0)
	{
		ReserveDFS(map,sign,Row,Col,curR,curC-1,curR,curC);
	}
	//右
	if (curC+1 < Col)
	{
		ReserveDFS(map,sign,Row,Col,curR,curC+1,curR,curC);
	}
}


int main()
{
	int R,C;
	cin>>R>>C;
	char map[DIM][DIM];
	bool sCanReach[DIM][DIM];//起点能到达的矩阵 
	bool tCanReach[DIM][DIM];//终点能到的矩阵  
	for (int i=0; i<R; i++)
	{
		for (int j=0; j<C; j++)
		{
			cin>>map[i][j];
		}
	}
	memset(sCanReach,0,DIM*DIM);
	memset(tCanReach,0,DIM*DIM);
	//处理
	for (int i=0; i<R; i++)
	{
		for (int j=0; j<C; j++)
		{
			if (map[i][j] == 'S')
			{
				ForwardDFS(map,sCanReach,R,C,i,j);
			}
		}
	}
	for (int i=0; i<R; i++)
	{
		for (int j=0; j<C; j++)
		{
			if (map[i][j] == 'T')
			{
				if (sCanReach[i][j] == false)
				{
					cout<<"I'm stuck!"<<endl;
					return 0;
				}
				ReserveDFS(map,tCanReach,R,C,i,j,i,j);
			}
		}
	}
	
	int num = 0;

/*	for (int i=0; i<R; i++)
	{
		for (int j=0; j<C; j++)
		{
			cout<<map[i][j]<<" ";
		}
		cout<<endl;
	}
	for (int i=0; i<R; i++)
	{
		for (int j=0; j<C; j++)
		{
			cout<<sCanReach[i][j]<<" ";
		}
		cout<<endl;
	}
	cout<<endl;
	for (int i=0; i<R; i++)
	{
		for (int j=0; j<C; j++)
		{
			cout<<tCanReach[i][j]<<" ";
		}
		cout<<endl;
	}
	cout<<endl;
*/
	for (int i=0; i<R; i++)
	{
		for (int j=0; j<C; j++)
		{
			if (map[i][j] != 'S' && map[i][j] != 'T' &&
				sCanReach[i][j]  && !tCanReach[i][j])
			{
				num++; 
			}
		}
	}
	cout<<num<<endl;
	return 0;
}

代码经CCF平台测试,时间花费为 0ms ,得分100分。


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值