0x01.问题
小明冒充X星球的骑士,进入了一个奇怪的城堡。城堡里边什么都没有,只有方形石头铺成的地面。
假设城堡地面是 n x n 个方格。
按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃。
每走到一个新方格,就要向正北方和正西方各射一箭。(城堡的西墙和北墙内各有 n 个靶子)
同一个方格只允许经过一次。但不必走完所有的方格。
如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?
有时是可以的,比如图中的例子。
本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)
输入:
第一行一个整数N(0<N<20),表示地面有 N x 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
注:本题来源于蓝桥杯2016年决赛--路径之谜。
0x02.分析问题
分析题目,发现需要满足的条件为:
- 从起点走到终点。
- 只能上下左右走。
- 不能走重复。
- 所有箭的数量需要相等。
- 题目保证存在一个有效路径。
这是一个在棋盘上寻找路径的问题,有些类似八皇后问题,优先考虑使用深度优先搜索。
需要注意的有:
- 由于不能重复,所以需要一个二维的flag来标记每个点。
- 每次走一步,需要先考虑箭的数量。
- 需要一个全局的数组记录路径。
- 回溯时要还原之前的状态。
有了这些关键,我们可以写出代码了。
0x03.解决代码--DFS
#include<stdio.h>
#include<stdlib.h>
int MAXSIZE;//边界大小
int flag[20][20];//标志变量,标记是否走过
int N[20],W[20],P[400];//N,W为题目的箭数,P为路径
int N1[20],W1[20];//为行走过程中产生的箭数
int pdex=0;//P的下标
int check()//检验箭的数量是否相等
{
for (int i = 0; i < MAXSIZE; i++)
{
if (N[i] != N1[i] || W[i] != W1[i])
{
return 0;
}
}
return 1;
}
int Step(int *x,int *y,int n)//行走一步
{
switch (n)
{
case 1://向右走
if (*x + 1 <= MAXSIZE - 1&&!flag[*y][*x+1]&&N[*x+1]>N1[*x+1]&&W[*y]>W1[*y])
{
*x=*x+1;
flag[*y][*x] = 1;
P[pdex++] = MAXSIZE * (*y) + *x;
return 1;
}
else
{
return 0;
}
break;
case 2://向左走
if (*x - 1 >= 0 && !flag[*y][*x-1] && N[*x-1] > N1[*x-1]&& W[*y] > W1[*y])
{
*x = *x - 1;
flag[*y][*x] = 1;
P[pdex++] = MAXSIZE * (*y) + *x;
return 1;
}
else
{
return 0;
}
break;
case 3://向上走
if (*y - 1 >= 0 && !flag[*y-1][*x] && N[*x] > N1[*x] && W[*y-1] > W1[*y-1])
{
*y=*y-1;
flag[*y][*x]=1;
P[pdex++] = MAXSIZE * (*y) + *x;
return 1;
}
else
{
return 0;
}
break;
case 4://向下走
if (*y + 1 <= MAXSIZE - 1 && !flag[*y+1][*x] && N[*x] > N1[*x] && W[*y+1] > W1[*y+1])
{
*y=*y+1;
flag[*y][*x] = 1;
P[pdex++] = MAXSIZE * (*y) + *x;
return 1;
}
else
{
return 0;
}
}
}
void print()
{
printf("0");
for (int i = 1; i < pdex; i++)
{
printf(" %d", P[i]);
}
}
void DFS(int x, int y)
{
if (x == MAXSIZE - 1 && y == MAXSIZE - 1)//走到边界
{
if (check())
{
P[pdex] = MAXSIZE * y + x;
print();//由于题目只存在唯一解,找到一个,就直接退出程序
exit(0);
}
return;
}
for (int i = 1; i <= 4; i++)
{
int temp1 = x;
int temp2 = y;
if (Step(&x,&y,i))
{
N1[x]++;
W1[y]++;
DFS(x, y);//返回后,需要恢复之前的状态,因为这是一个回溯的过程
x = temp1;
y = temp2;
pdex--;
switch (i)
{
case 1:
N1[temp1+1]--;
W1[temp2]--;
flag[temp2][temp1 + 1] = 0;
break;
case 2:
N1[temp1 - 1]--;
W1[temp2]--;
flag[temp2][temp1 - 1] = 0;
break;
case 3:
N1[temp1]--;
W1[temp2 - 1]--;
flag[temp2 - 1][temp1] = 0;
break;
case 4:
N1[temp1]--;
W1[temp2 + 1]--;
flag[temp2 + 1][temp1] = 0;
break;
}
}
else
{
continue;
}
}
return;
}
int main()
{
int n,i,j,num;
scanf("%d", &n);
MAXSIZE = n;
for (i = 0; i < MAXSIZE; i++)
{
scanf("%d", &num);
N[i] = num;
N1[i] = 0;
}
for (i = 0; i < MAXSIZE; i++)
{
scanf("%d", &num);
W[i] = num;
W1[i] = 0;
}
N1[0] = W1[0] = 1;//进去就要射两箭
for (i = 0; i < MAXSIZE; i++)
{
for (j = 0; j < MAXSIZE; j++)
{
flag[i][j] = 0;
}
}
flag[0][0] = 1;
P[pdex++] = 0;
DFS(0, 0);
return 0;
}