前言
蓝桥杯2016年国赛,编程题(C++)
考察知识点:DFS
一、题目描述
题目描述
小明冒充 X 星球的骑士,进入了一个奇怪的城堡。
城堡里边什么都没有,只有方形石头铺成的地面。
假设城堡地面是 n×n 个方格。如下图所示。
![](https://i-blog.csdnimg.cn/blog_migrate/3d3b85145dcab7f475213513063a8576.png)
按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃。每走到一个新方格,就要向正北方和正西方各射一箭。(城堡的西墙和北墙内各有 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;
}