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


问题分析

性质1显而易见,使用DFS(BFS也行)即可找到从起点S出发能到达的所有点,将能到达的点记录下来:s_arrived[x][y] = true;

问题的难点在于性质2,暴力的做法可以遍历所有S可以到达的点({(x,y)|s_arrived[x][y] = true}),对每个点进行DFS(BFS也行)看其能否到达终点T并计数。但这样时间复杂度较高,下面给出另一种方法。

既然要找到不能到达T的点,那么我们可以先找能到达T的点并记录下来:t_arrived[x][y] = true;那么,什么点能到达T呢?便于理解,举例如下(A、B、C都是点,例子中不关心其坐标):

存在一条路径:S-->A-->B-->C-->T

第一步,我们可以先找C,显然它与T相邻(启发:第一步找与T相邻的点);第二步,找能到达C的点,显然它与C相邻;从这2步可以看出貌似可以递归地来找到所有能到达T的点,再深入想就会发现递归实现其实就是从T开始进行DFS,具体细节描述如下(为了叙述简便,把能到达X的点统称为destination(X)):

1.找到所有与X相邻的点,显然他们都是destination(X)的候选者;

2.检查这些候选者能否到达X,去掉那些不能到达X的候选者,剩下的就是可以到达X的点,即destination(X)。使用题中给出的样例作为例子:

T左边的(4,4)和T下边的(5,5)都可以作为X(令X=T)的destination的候选者,我们只需去掉那些不能到X的候选者,如(5,5),因为它是'.',所以它只能向下,不能到达T。

3.destination(T)、destination( destination(T) )、destination( destination( destination(T) ) )……即为所有能到达T的点。

求解过程中需要的量:X、与X相邻的候选者

候选者筛选规则:若候选者为‘-’,则需保证X与候选者在同一行;若候选者为‘|’,则需保证X与候选者在同一列;若候选者为‘.’,则需保证X在候选者的下方。

最后,符合性质1和性质2的点为:{(x,y)|s_arrived[x][y] = true && t_arrived[x][y] = false}.

代码

#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;

const int N = 50;

char map[N+1][N+1];
bool ban[N+1][N+1] = {false};
bool s_arrived[N+1][N+1] = {false};
bool t_arrived[N+1][N+1] = {false};
int r,c;

const int plus_sign[][2] = {{-1,0},{0,1},{1,0},{0,-1}};
const int minus_sign[][2] = {{0,-1},{0,1}};
const int stick_sign[][2] = {{-1,0},{1,0}};
const int dot_sign[][2] = {{1,0}};

bool IsLegal(int x,int y){
	if(x<1||x>r||y<1||y>c)
		return false;
	return true;
}

//int test_s = 0;
//int test_t = 0;

void dfs_s(int x,int y){
	if(!IsLegal(x,y)||ban[x][y]||s_arrived[x][y])
		return;
	s_arrived[x][y] = true;
//	for(int i=0;i<test_s;i++)
//		printf("\t");
//	test_s++;
//	printf("dfs_s(%d,%d)\n",x,y);
	switch(map[x][y]){
		case '+':
			for(int i=0;i<4;i++)
				dfs_s(x+plus_sign[i][0],y+plus_sign[i][1]);
			break;
		case '-':
			for(int i=0;i<2;i++)
				dfs_s(x+minus_sign[i][0],y+minus_sign[i][1]);
			break;
		case '|':
			for(int i=0;i<2;i++)
				dfs_s(x+stick_sign[i][0],y+stick_sign[i][1]);
			break;
		case '.':
			dfs_s(x+dot_sign[0][0],y+dot_sign[0][1]);
			break;
		case 'S':
			for(int i=0;i<4;i++)
				dfs_s(x+plus_sign[i][0],y+plus_sign[i][1]);
			break;
		case 'T':
			for(int i=0;i<4;i++)
				dfs_s(x+plus_sign[i][0],y+plus_sign[i][1]);
			break;
		default:
			printf("error!\n");
	}
//	test_s--;
}

void dfs_t(int x,int y,int last_x,int last_y){//last_x,last_y记录前一层(dfs)的点的x,y,用于分析反向情况 
	if(!IsLegal(x,y)||ban[x][y]||t_arrived[x][y])
		return;
	
//	for(int i=0;i<test_t;i++)
//		printf("\t");
//	test_t++;
//	printf("dfs_t(%d,%d)\n",x,y);
	if(map[x][y]=='-'&&last_x!=x)//保证同一行(不在同一行就return)
		return;
	else if(map[x][y]=='|'&&last_y!=y)//保证同一列(不在同一列就return)
		return;
	else if(map[x][y]=='.'&&last_x!=x+1)//保证last在x的下方(否则就return)
		return;
	t_arrived[x][y] = true;
	for(int i=0;i<4;i++)
		dfs_t(x+plus_sign[i][0],y+plus_sign[i][1],x,y);
	
//	test_t--;
}

int main(){
	int s_x,s_y,t_x,t_y,cnt = 0;
	
	scanf("%d %d",&r,&c);
	for(int i=1;i<=r;i++){
		getchar();
		for(int j=1;j<=c;j++){
			scanf("%c",&map[i][j]);	
			if(map[i][j]=='#')
				ban[i][j] = true;
			else if(map[i][j]=='S'){
				s_x = i;
				s_y = j;
			}
			else if(map[i][j]=='T'){
				t_x = i;
				t_y = j;
			}
		}
	}
	
	//先从S开始找到所有能到达的点,结果存在s_arrived中 
	dfs_s(s_x,s_y);
	if(!s_arrived[t_x][t_y]){
		printf("I'm stuck!\n");
		return 0;
	}
	dfs_t(t_x,t_y,t_x,t_y);
	
	for(int i=1;i<=r;i++)
		for(int j=1;j<=c;j++)
			if(s_arrived[i][j]&&!t_arrived[i][j])
				cnt++;
	printf("%d\n",cnt);
	
	return 0;
}

注意事项

1、代码的注释部分完全可以忽略,如果加上可以查看DFS的具体过程。

2、plus_sign[][2]、minus_sign[][2]、stick_sign[][2]和dot_sign[][2]这四个二维数组对应'+'、'-'、'|'、'.' 四种情况,用来确定允许进一步搜索的方向,完全可以使用if-else语句替换。

3、dfs_t的后2个参数记录的是问题分析中X的坐标。


有疑问欢迎提出!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用是一段C++代码,用来解析CCF201612-2工资计算问题。这段代码通过计算各种收入范围和税率,然后根据输入的税前工资来计算出税后工资。具体步骤如下: 1. 首先定义了salaryrange数组存储各个收入范围,taxrate数组存储各个税率,以及range数组用来计算各种收入范围。 2. 通过循环计算range数组的值,range[i表示税前工资在salaryrange[i范围内的税后工资。 3. 输入税前工资t后,通过循环找到税前工资所在的收入范围i。 4. 如果i=0,表示税前工资在最低收入范围内,税后工资直接等于税前工资。 5. 如果i不等于0,表示税前工资在其他收入范围内,通过公式s = salaryrange[i-1 + (t - range[i-1]) * 100 / (100 - taxrate[i-1])计算税后工资。 6. 最后输出税后工资s。 根据这段代码的解析,可以计算出CCF201612-2工资计算问题的答案。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [CCF201612-2 工资计算(100分)](https://blog.csdn.net/tigerisland45/article/details/54799557)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [CCF201612-2 工资计算](https://blog.csdn.net/qq_40670344/article/details/97441803)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值