路径之谜
小明冒充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;
}