路径之谜--DFS解决

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.分析问题

分析题目,发现需要满足的条件为:

  1. 从起点走到终点。
  2. 只能上下左右走。
  3. 不能走重复。
  4. 所有箭的数量需要相等。
  5. 题目保证存在一个有效路径。

这是一个在棋盘上寻找路径的问题,有些类似八皇后问题,优先考虑使用深度优先搜索。

需要注意的有:

  1. 由于不能重复,所以需要一个二维的flag来标记每个点。
  2. 每次走一步,需要先考虑箭的数量。
  3. 需要一个全局的数组记录路径。
  4. 回溯时要还原之前的状态。

有了这些关键,我们可以写出代码了。

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;
}
发布了102 篇原创文章 · 获赞 115 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ATFWUS/article/details/104845743