算法练习题43---蓝桥杯2016国赛-路径之谜

本文介绍了2016年蓝桥杯国赛的一道编程题,题目要求通过DFS算法找出从城堡西北角到东南角的唯一路径,给定箭靶上的数字作为条件。文章详细阐述了题目描述、输入输出样例、解题思路,并提供了完整的C++代码实现,包括边界判断、回退处理等关键步骤。
摘要由CSDN通过智能技术生成

前言

蓝桥杯2016年国赛,编程题(C++)

考察知识点:DFS

赛题链接:路径之谜 - 蓝桥云课 (lanqiao.cn)

一、题目描述

题目描述

小明冒充 X 星球的骑士,进入了一个奇怪的城堡。

城堡里边什么都没有,只有方形石头铺成的地面。

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

按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃。每走到一个新方格,就要向正北方和正西方各射一箭。(城堡的西墙和北墙内各有 n 个靶子)同一个方格只允许经过一次。但不必走完所有的方格。如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?有时是可以的,比如上图中的例子。

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

输入描述

第一行一个整数 N (0≤N≤20),表示地面有 N×N 个方格。

第二行 N 个整数,空格分开,表示北边的箭靶上的数字(自西向东)

第三行 N 个整数,空格分开,表示西边的箭靶上的数字(自北向南)

输出描述

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

为了方便表示,我们约定每个小格子用一个数字代表,从西北角开始编号: 0,1,2,3 ⋯

比如,上图中的方块编号为:

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

运行限制

  • 最大运行时间:5s
  • 最大运行内存: 256M

二、思路

这个题目是一个比较经典的寻路问题,但是难度要略大于简单的寻路问题。

寻路问题的首要目标就是找到一个可达路径,具体的方法就是,利用for循环配合DFS去进行不断递归,最终找到终点。也就是说,核心有两个,for循环控制前进方向,DFS控制递归的深入。

但是这个题目有个附加的条件,就是每次移动,都会想正北和正西(也就是第一行和第一列的靶)上射箭,这就需要我们在移动的时候,不仅仅要判断是不是到了边界或者重复走过了某些点,还需要判断箭靶上的可用容量是不是为0(为0就是已经将箭靶上的容量全部填满)。

因此最终满足的条件为:到达左下角的终点,箭靶上的数字全为0,输出走过的完整路径。

我们设棋盘大小为N,初始坐标为(r,c)即(1,1),那么最终到达为(N,N)即r== N,c== N,另外,此时的所有水平方向箭靶即b[N]均为0,竖直方向箭靶即a[N]均为0,然后我们去输出走过的路径即可。走过的路径我这里用vector数组来存储,每走过一个位置,就放到vector数组中。

**关于回退处理:**当我们遍历过一个节点后,一定要注意回退,也就是还原到进入该节点前的状态,例如,标记走过该节点的标志flag要重置,箭靶上的容量要+1,vector数组中刚刚填入的数字要清空。

三、具体代码

#include<bits/stdc++.h>
using namespace std;
struct point
{
    int row;
    int column;
};
vector<point> res;
int a[25];  //竖直方向上的箭,由r控制
int b[25];  //水平方向上的箭,由c控制
int n;
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
int flag[25][25];
int finded=0;
bool check(int r,int c)
{
	if(r==n&&c==n)
	{
		for(int i=1;i<=n;i++)
		{
			if(a[i]!=0)
			{
				return true;
			}
		}
		for(int j=1;j<=n;j++)
		{
			if(b[j]!=0)
			{
				return true;
			}
		}
		
		for(int i=0; i<res.size(); i++)
        {
            int x=res[i].row;
            //x 轴坐标
            int y=res[i].column;
            //y 轴坐标
            int sum=n*(x-1)+y-1 ;
            // 通过计算的到为题目要求的坐标系
            cout <<sum<< " ";
        }
        finded=1;
        cout << endl;
		return true;
	}
	return false;
}

bool judge(int x,int y) //边界判断
{

    if(flag[x][y]==1) //已被走过,不能再走,超出边界
        return 0;
    
    else if(x<1) //从左侧走出方格
        return 0;
    
    else if(x>n) //从右侧走出方格
        return 0;
    
    else if(y<1) //从上侧走出方格
        return 0;
    
    else if(y>n) //从下侧走出方格
        return 0;
    
    else if(a[x]<=0) //没走到右下角,箭用完了
        return 0;
    
    else if(b[y]<=0) //没走到右下角,箭用完了
        return 0;
    
    else return 1; //符合边界条件,可以继续执行搜索
    
}

void dfs(int r,int c)
{
	if(finded==1)
	{
		return;
	}
	if(check(r,c))
	{
		return;
	}
	int temp_r,temp_c;
	for(int i=0;i<4;i++)  //四个方向,上,右,下,左
	{
		temp_r=r+dir[i][0];
		temp_c=c+dir[i][1];
		if(!judge(temp_r,temp_c))
        {
            continue ; //不符合要求继续换方向搜索
        }
        flag[temp_r][temp_c]=1;
        a[temp_r]--;
		b[temp_c]--;
		res.push_back({temp_r,temp_c});
		
		dfs(temp_r,temp_c);
		
		res.pop_back();  //回退处理,将刚才装入vector的节点删掉
		flag[temp_r][temp_c]=0;  //回退处理
		a[temp_r]++;  //回退处理
		b[temp_c]++;  //回退处理
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>b[i];  //先输入水平方向上的箭
	}
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];  //再输入竖直方向上的箭
	}
	flag[1][1]=1;
	a[1]--;
	b[1]--;
	res.push_back({1,1});
	dfs(1,1);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨大熊的代码世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值