利用深度优先搜索(dfs)来解决迷宫问题

利用深度优先搜索(dfs)来解决迷宫问题

一.深度优先搜索(dfs)

1.什么叫dfs

深度优先搜索类似于树的先序遍历
是利用栈或者递归的方式实现的,体现出了后进先出的特点;
通俗来说就是一次访问一条路,一直朝着一个方向探索,直到遇到死路退回到前一个分支,继续探索;
一般来说,深度搜索解决的问题主要为寻求所有解和连通性

2.遍历过程

(1)从图中某个初始顶点v出发,首先访问初始顶点v。
(2)然后依次从v的未被访问的邻接点w,再从w出发进行深度优先遍历,直到图中所有与v有路径相通的的顶点都被访问过为止。

3.算法设计

解决问题:
(1)如何确定一个顶点是否访问过?
设置一个visited[]全局数组,
visited[i]=0表示顶点i没有访问;
visited[i]=1表示顶点i已经访问过。
(在图中也可以修改图本身来实现)

4.dfs算法模板

void dfs(int s)
{
	if(找到解了)
	{
		相应的操作;
		return}
	尝试每一种可能
	{
		if(满足条件)
		{
			标记走过;
			进行下一步dfs;
			回溯一步;	//恢复原状态
		}
	}
}

二.例题示范

棋盘问题

(1)问题描述:

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
(2)输入:

输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
(3)输出:

对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
(4)样例输入:

2 1
#.
.#
4 4
…#
…#.
.#…
#…
-1 -1

(5)样例输出

2
1

#include<stdio.h>
#include<string.h>
int n,k;
char map[10][10];
int sum;
int visited[10];
void dfs(int y,int t)
{
	if(t==k)
	{
		sum++;
		return ;
	}
		//满足条件,次数加1,返回;
	if(y<=n)
	dfs(y+1,t);
		//一直调用到最后一行,从最后一行开始,
		//考虑到每一种情况;
	for(int i=1;i<=n;i++)
	{
		if(map[y][i]=='#'&&visited[i]==0)
		{	//需要满足的条件
			visited[i]=1;
				//标记已经走过
			dfs(y+1,t+1);
				//继续下一步dfs
			visited[i]=0;
				//恢复初始状态
		}
	}
}
int main()
{
	while(1)
	{
		sum=0;
		scanf("%d%d",&n,&k);
		if(n==-1&&k==-1)
			break;
			memset(map,0,sizeof(map));
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			scanf(" %c",&map[i][j]);
		dfs(1,0);
			//初始化y为第一行,次数为0
		printf("%d\n",sum);
	}
	return 0;
}

三.代码

	//用dfs来计算最短路径
#include<stdio.h>
int r,c,num=10000;
char map[41][41];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
void dfs(int x,int y,int k)
{
	if(x==r&&y==c)
	{
		if(num>k)
		{
			num=k;
		}
	}
	for(int i=0;i<4;i++)
	{
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1&&nx<=c&&ny>=1&&ny<=r&&map[ny][nx]=='.')
		{
			map[ny][nx]='#';
			dfs(nx,ny,k+1);
			map[ny][nx]='.';
		}
	}
}
int main()
{
	scanf("%d%d",&r,&c);
	for(int i=1;i<=r;i++)
	{
		for(int j=1;j<=c;j++)
		{
			scanf(" %c",&map[i][j]);
		}
	}
	map[1][1]='#';
	dfs(1,1,1);
	printf("%d",num);
}
//dfs遍历是否能够从(ha,la)到(hb,lb)
#include<stdio.h>
#include<string.h>
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
int n,ha,la,hb,lb,nx,ny;
bool flag;
char map[200][200];
void dfs(int x,int y)
{
	for(int i=0;i<4;i++)
	{
		nx=x+dx[i];
		ny=y+dy[i];
		if(nx>=0&&nx<n&&ny>=0&&ny<n&&map[nx][ny]=='.')
		{
			map[nx][ny]='#';
			if(nx==hb&&ny==lb)
			{
				printf("YES\n");
				flag=true;
				break;
			}
			else
				dfs(nx,ny);
		}
	}
}
int main()
{
	int k;
	scanf("%d",&k);
	for(int i=0;i<k;i++)
	{
		flag=false;
		scanf("%d",&n);
		memset(map,'#',sizeof(map));
		for(int j=0;j<n;j++)
			for(int h=0;h<n;h++)
			{
				scanf(" %c",&map[j][h]);
			}
		scanf("%d%d%d%d",&ha,&la,&hb,&lb);
		if(map[ha][la]=='#'||map[hb][lb]=='#')
		{
			printf("NO\n");
			continue; 
		}
		else
		{
			map[ha][la]='#';
			//标记起点走过
			dfs(ha,la);
		}
		if(!flag)
		printf("NO\n");
	}
	return 0;
}
//棋盘问题代码
#include<stdio.h>
#include<string.h>
int n,k;
char map[10][10];
int sum;
int visited[10];
void dfs(int y,int t)
{
	if(t==k)
	{
		sum++;
		return ;
	}
	if(y<=n)
	dfs(y+1,t);
	for(int i=1;i<=n;i++)
	{
		if(map[y][i]=='#'&&visited[i]==0)
		{
			visited[i]=1;
			dfs(y+1,t+1);
			visited[i]=0;
		}
	}
}
int main()
{
	while(1)
	{
		sum=0;
		scanf("%d%d",&n,&k);
		if(n==-1&&k==-1)
			break;
			memset(map,0,sizeof(map));
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			scanf(" %c",&map[i][j]);
		dfs(1,0);
		printf("%d\n",sum);
	}
	return 0;
}