第八届蓝桥杯-2017-决赛A组-c++题解②路径之谜

本篇介绍了一个有趣的编程挑战,通过给定的靶子上箭的数量,利用DFS+剪枝算法来推断骑士在城堡内的行走路径。算法确保路径的唯一性,提供了一种解决此类问题的有效方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

路径之谜


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

假设城堡地面是 n x n 个方格。【如图1.png】所示。

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


思路:DFS+剪枝

①建一个表,(i:1->N)arr[i][0]存西边箭数,arr[0][i]存北边箭数,中间部分为0或1用来表示是否是旧点;

②从(1,1)开始深搜,目标为(N,N)。深搜过程中每到一个点,将该点标记为旧点,对应位置的靶子的箭的数量自减1,并将走到的这个点存入最后会输出的ans[],然后向这个点的上下左右继续深搜。

③我使用了三种剪枝,详见代码,挺好理解的。

④到达终点的条件:访问表arr,若北边靶的箭数量+西边靶的箭的数量=2(即只剩下N,N的箭没有拔),则找了目标路径,否则继续深搜知道找到为止。


代码:

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

int arr[22][22]={0}; //(i-1)*N + j - 1 
int ans[410];
int count_ans=0;
int N;

 bool dfs(int i, int j)
 {
 	if(i == N && j == N)
 	{
 		//判断是否所有箭都中了
 		int flag=0;
		for(int k=1; k<=N; k++)
		{
			flag+=arr[k][0];
			flag+=arr[0][k];
		 	if(flag>2)
		 		break;
		}
		//已经找到这条路了 
		if(flag == 2) 
		{
			for(int k=0; k<count_ans; k++)
				cout<<ans[k]<<" ";
			cout<<N*N-1; 
			return true;
		} 
		else
			return false;
	}
	
	if(i<1 || i>N || j<1 || j>N)//剪枝3:超出范围 
		return false; 
	if(arr[i][j] != 0)//剪枝1:走过
		return false;
	if(arr[i][0] == 0 || arr[0][j] == 0)//剪枝2:箭不够了
		return false;
	
 	arr[i][j]=1;//设为旧点,拔出箭 ,放入答案栈 
 	arr[i][0]--;
 	arr[0][j]--;
 	ans[count_ans++]= (i-1)*N + j - 1 ;
 	if(dfs(i+1,j)||dfs(i-1,j)||dfs(i,j+1)||dfs(i,j-1)) 
 		return true;
 	count_ans--;//复原 
 	arr[i][0]++;
 	arr[0][j]++;
 	arr[i][j]=0;
 	return false;
 }

int main()
{
	cin>>N;
	for(int i=0; i<N; i++)
		cin>>arr[0][i+1];
	for(int i=0; i<N; i++)
		cin>>arr[i+1][0];

	dfs(1,1);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值