蓝桥杯真题 路径之谜【第七届】【决赛】【C组】(C语言实现)

小明冒充X星球的骑士,进入了一个奇怪的城堡。
  城堡里边什么都没有,只有方形石头铺成的地面。

  假设城堡地面是 n x n 个方格。如下图所示:
 



  按习俗,骑士要从西北角走到东南角。
  可以横向或纵向移动,但不能斜着走,也不能跳跃。
  每走到一个新方格,就要向正北方和正西方各射一箭。
  (城堡的西墙和北墙内各有 n 个靶子)


  同一个方格只允许经过一次。但不必做完所有的方格。

  如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?

  有时是可以的,比如图1.png中的例子。

  本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)

输入格式

  第一行一个整数N(0<N<20),表示地面有 N x N 个方格
  第二行N个整数,空格分开,表示北边的箭靶上的数字(自西向东)
  第三行N个整数,空格分开,表示西边的箭靶上的数字(自北向南)

输出格式

  一行若干个整数,表示骑士路径。

  为了方便表示,我们约定每个小格子用一个数字代表,从西北角开始编号: 0,1,2,3....
  比如,图1.png中的方块编号为:

  0 1 2 3
  4 5 6 7
  8 9 10 11
  12 13 14 15


  示例:
  用户输入:
  4
  2 4 3 4
  4 3 3 3

  程序应该输出:
  0 4 5 1 2 3 7 11 10 9 13 14 15

思路如下:

这是一道深度优先搜索的题目,直接套用模板就行,如果不会,下面会以这一题为例详细解释。

首先看题目的要求。

有两点。第一,我们需要从地图的左上角走到地图的右下角;第二,当我们走到了一个格子的时候,我们需要向当前格子的上方和左方射一只弓箭,最后到达终点时,每个靶子上的弓箭恰好和题目给定的靶子相同。这样才算是题目所说的正确路线。

我们首先说第一个问题解决思路。

我们把问题简化,简化成只要找到一条路,能从地图的左下角走到右下角就行。

怎么找?穷举!当你处于一个格子的时候,你能走的方向只有上下左右四个方向,那么就每个方向试一试。当你发现向下走到了一个格子(假设),这个格子走不下去了,或者到达不了终点,我们就退回去,从上一个格子的另一个方向试一试(如果还有其他方向没有试过)。如果这个格子也走不下去,再次退回,从上一个格子的上一个继续尝试。这样一步一步穷举,总能找出最后合理的路线。(代码稍后解释)这就是所谓的回溯。

接下来,我们说第二个问题的解决思路

当我们走到一格,就得向上方和右方射一箭。我们可以运用两个数组分别表示上方的标靶和左方的标靶,然后每走一格,就在对应位置加一,当到了终点,我们将这两个数组的值与题目给定的值对比,一样就输出结束,不一样就回溯,找另一条路。

接下来解释代码

首先是关于初始化的问题

首先得先将地图给构建起来,我们将用一个二维数组来存储地图。但是当你走到一个格子的时候

这个格子在这条路线上就不应该再走,所以我们还得需要标记。那么就可以用三维数组来表示整个地图。

    int n;//题目给定迷宫大小
	scanf("%d" , &n);
	int map[n][n][2];//迷宫,1好代表格子编号,2号代表标记
	int b[n];//代表上方(也就是北方)箭靶
	int x[n];//代表左方(也就是西方)箭靶
void chushihua(int gs , int map[][gs][2] , int b[] , int x[])//初始化函数
{
	int i;
	for(i = 0;i < gs;i ++)
	{
		scanf("%d" , b ++);//输入题目要求的箭靶上弓箭个数
	}
	for(i = 0;i < gs;i ++)
	{
		scanf("%d" , x ++);//输入题目要求的箭靶上弓箭个数
	}
	
	int j;
	for(i = 0;i < gs;i ++)//对迷宫进行初始化
	{
		for(j = 0;j < gs;j ++)
		{
			map[i][j][0] = i * gs + j;//这边是对格子编号的初始化,因为是按顺序来编号的,所以存在一个二维数组编号转换到一维数组编号的过程,这里不再赘述
			map[i][j][1] = 0;//地图标记初始化
		}
	}
}

地图构建起来之后,我们就需要工具去探索地图

    int fangxiang[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};//这是一个方向矩阵,后面会讲
	int jl_b[n];//记录上方弓箭个数
	int jl_x[n];//记录左方弓箭个数
	gui_0(n , jl_b);//初始化箭靶
	gui_0(n , jl_x);
	int jilu_gs = 1;//记录正确路线的长度
	int jilu_lx[400] = {0};//记录正确路线的编号
void gui_0(int gs , int sz[])//初始化箭靶的函数
{
	sz[0] = 1;//因为初始位置在(0,0)所以一号位得初始化成1
	sz ++;
	while(gs -- > 0)
	{
		*sz ++ = 0;
	}
}

接下来解释实现走迷宫的函数(最核心的代码)

首先我们先来解析这个函数的参数有什么。按顺序就是:地图的大小,地图,记录上方箭靶,上方标准箭靶,记录左方箭靶,左方标准箭靶,路线的长度,记录路线,当前所处的行数,当前所处的列数。

enum
{
	MZ = 0,//没走
	ZL = 1,//走了
};

int pd_sf_yj(int gs , int hang , int lie)//边界判断
{
	return (hang >= 0 && hang < gs) && (lie >= 0 && lie < gs);
}

void search(int gs , int map[][gs][2] , int jl_b[] , int b[] , int jl_x[] , int x[] , int * jl_cs , int lx[] , int fx[4][2] , int i , int j)
{
	if(i == gs - 1 && j == gs - 1)//如果走到了终点
	{
		if(pd_sf_hl(gs , jl_b , b , jl_x , x))//判断当前的箭靶是否合理
		{
			shuchu(*jl_cs , lx + 1);//合理,输出路线编号
			exit(0);//退出程序
		}
		return ;
	}
	
	map[i][j][1] = ZL;//这个表示当前走的这一格,因为已经走了,所以得标记上1(这边用了枚举变量)
	int t;
	for(t = 0;t < 4;t ++)//一个格子有四个方向,所以得循环四次
	{
		int hang = i + fx[t][0];//这里的两行代码得读者自己想一想
		int lie  = j + fx[t][1];//达到的结果就是每一次循环都能得到当前格子四个方向的格子行和列
		if(pd_sf_yj(gs , hang , lie) && map[hang][lie][1] == MZ && (jl_b[lie] + 1 <= b[lie] && jl_x[hang] + 1 <= x[hang]))//这里需要判断4个条件,首先是边界判断,然后是要走的格子有没有标记,以及如果走了下一个格子之后箭靶是否会超过标准箭靶
		{//接下来就是走的环节
			++ jl_b[lie];//上箭靶射了一剑
			++ jl_x[hang];//左箭靶射了一箭
			++ *jl_cs;//走了一格
			lx[*jl_cs] = map[hang][lie][0];//将走的这一格编号加入记录路线
			search(gs , map , jl_b , b , jl_x , x , jl_cs , lx , fx , hang , lie);//递归找下一格,如果不是就回溯
			-- *jl_cs;//回溯时将箭靶上的箭退回
			-- jl_b[lie];
			-- jl_x[hang];
		}
	}
	map[i][j][1] = MZ;//如果这一格四周走都不行,那就整个退出
	return ;
}

大体流程就是这样,穷举路线,记录箭靶,然后和题目给的结果对比

完整代码

#include <stdio.h>
#include <stdlib.h>
enum
{
	MZ = 0,
	ZL = 1,
};
void chushihua(int gs , int [][gs][2] , int [] , int []);
void gui_0(int , int []);
void search(int gs , int [][gs][2] , int [] , int [] , int [] , int [] , int * , int [] , int [4][2] , int , int);
int pd_sf_hl(int , int [] , int [] , int [] , int []);
int pd_xt(int , int [] , int []);
void shuchu(int , int []);
int pd_sf_yj(int , int , int);
int main (void)
{
	int n;
	scanf("%d" , &n);
	int map[n][n][2];
	int b[n];
	int x[n];
	chushihua(n , map , b , x);
	
	int fangxiang[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
	int jl_b[n];
	int jl_x[n];
	gui_0(n , jl_b);
	gui_0(n , jl_x);
	
	int jilu_gs = 1;
	int jilu_lx[400] = {0};
	search(n , map , jl_b , b , jl_x , x , &jilu_gs , jilu_lx , fangxiang , 0 , 0);
	return 0;
}

int pd_sf_yj(int gs , int hang , int lie)
{
	return (hang >= 0 && hang < gs) && (lie >= 0 && lie < gs);
}

void shuchu(int gs , int sz[])
{
	while(gs -- > 0)
	{
		printf("%d " , *sz ++);
	}
	putchar('\n');
}

int pd_xt(int gs , int s1[] , int s2[])
{
	while(gs -- > 0)
	{
		if(*s1 != *s2)
		{
			return 0;
		}
		s1 ++;
		s2 ++;
	}
	return 1;
}

int pd_sf_hl(int gs , int jl_b[] , int b[] , int jl_x[] , int x[])
{
	return pd_xt(gs , jl_b , b) && pd_xt(gs , jl_x , x);
}

void search(int gs , int map[][gs][2] , int jl_b[] , int b[] , int jl_x[] , int x[] , int * jl_cs , int lx[] , int fx[4][2] , int i , int j)
{
	if(i == gs - 1 && j == gs - 1)
	{
		if(pd_sf_hl(gs , jl_b , b , jl_x , x))
		{
			shuchu(*jl_cs , lx + 1);
			exit(0);
		}
		return ;
	}
	
	map[i][j][1] = ZL;
	int t;
	for(t = 0;t < 4;t ++)
	{
		int hang = i + fx[t][0];
		int lie  = j + fx[t][1];
		if(pd_sf_yj(gs , hang , lie) && map[hang][lie][1] == MZ && (jl_b[lie] + 1 <= b[lie] && jl_x[hang] + 1 <= x[hang]))
		{
			++ jl_b[lie];
			++ jl_x[hang];
			++ *jl_cs;
			lx[*jl_cs] = map[hang][lie][0];
			search(gs , map , jl_b , b , jl_x , x , jl_cs , lx , fx , hang , lie);
			-- *jl_cs;
			-- jl_b[lie];
			-- jl_x[hang];
		}
	}
	map[i][j][1] = MZ;
	return ;
}

void gui_0(int gs , int sz[])
{
	sz[0] = 1;
	sz ++;
	while(gs -- > 0)
	{
		*sz ++ = 0;
	}
}

void chushihua(int gs , int map[][gs][2] , int b[] , int x[])
{
	int i;
	for(i = 0;i < gs;i ++)
	{
		scanf("%d" , b ++);
	}
	for(i = 0;i < gs;i ++)
	{
		scanf("%d" , x ++);
	}
	
	int j;
	for(i = 0;i < gs;i ++)
	{
		for(j = 0;j < gs;j ++)
		{
			map[i][j][0] = i * gs + j;
			map[i][j][1] = 0;
		}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值