dfs总结

25 篇文章 0 订阅

最近在做学校出的蓝桥杯的初赛试题,发现很多题都可以用dfs暴力解决。本文将总结一下dfs常用的两种题型(全排列问题,迷宫问题),最后根据这两种题型总结出一个dfs的模版。


首先要明确一点的就是:dfs()函数的功能是解决当前应该怎么办

对于全排列问题:当前应该解决的问题就是:当前这一位应该放什么数字进去(1~9);

对于迷宫问题:当前要解决的问题是:在当前这个点我可以往哪里走(4个方向:上下左右)。


(下面注释中带“**”的注释是重点语句)

①dfs解决全排列问题

典型题目:用数字 1~5进行全排列,显示出每一种情况并计算出总共有多少种情况。

#include <iostream>
using namespace std;

const int n = 5;
int ans = 0;
int a[n+1];
int book[n] = {0};  //0 表示还没用 

void bfs(int step) 
{
	if (step==n+1)  // **边界条件 
	{
		ans++;	//统计情况总数 
		for (int j=1;j<=n;j++)  //显示每种可能 
			cout<<a[j]<<" ";
		cout<<endl;
		return;  //  返回上一步 (这里的返回很重要) 
	}
	
	for (int i=1;i<=n;i++)  //**遍历所有可能的数字 
	{
		if (book[i]==0)  //**判断情况是否已经处理过 
		{  
			a[step] = i;  //**尝试这个数字 
			book[i] = 1;  //**把数字标记为【已尝试】 
			bfs(step+1);  //**递归下一个位 
			book[i] = 0;  //**尝试结束,取消标记 
		}
	}
	
	return;	 //返回 
}

int main()
{
	bfs(1);  //如果参数是 1,则边界条件为 n+1;如果是 0,边界则是 n 
	 
	cout<<ans<<endl;
	return 0;
}

  ②dfs解决迷宫问题

题目:给出迷宫大小、迷宫简图、起点坐标,终点坐标。求到达终点的路径数和最短路径所需的步数。

#include <iostream>
using namespace std;

int maze[10][10];  
int book[10][10] = {0};  // 标记该坐标是否走过 
int dx[4] = {1,0,-1,0};  //方向 
int dy[4] = {0,1,0,-1};

int ans = 0;  //到达终点的路线数目 
int minn = 99999;  // 最小步数 
int m,n;  // 迷宫大小
int sx,sy;  //起点 
int gx,gy;  // 终点  

void dfs(int x,int y,int step)
{
	if (x==gx && y==gy)  // **边界条件 
	{
		ans++; 
		if (step < minn) minn = step;
		return;  //返回上一步 (这里的返回很重要) 
	}
	
	for (int k=0;k<4;k++)  // **遍历所有情况(枚举 4 个方向的走法 
	{
		int nx = x + dx[k];
		int ny = y + dy[k];
		
		// **判断当前情况是否可用 
		if (0<=nx && nx<m && 0<=ny && ny<n)  //判断是否有越界 
		{
			if (book[nx][ny] == 0 && maze[nx][ny] == 0)  // 判断是否已经走过或者是否是障碍
			{
				book[nx][ny] = 1;  //**标记这个点已经用过(走过) 
				dfs(nx,ny,step+1); //**递归 
				book[nx][ny] = 0;  //**尝试结束,取消这个点的标记 
			} 
		}
	}
	
	return;
}


int main()
{
	int i,j;
	cin>>m>>n;  // 迷宫的行,列 
	cin>>sx>>sy>>gx>>gy;  //输入起点坐标和终点坐标 
	for (i=0;i<m;i++)
		for (j=0;j<n;j++)
			cin>>maze[i][j];

	book[0][0] = 1;  // (0,0)为起点,默认为已经走过		
	
	dfs(sx,sy,0);  //
	
	cout<<"路线数目: "<<ans<<endl;
	cout<<"最短路径: "<<minn<<endl; 
	
	return 0;
}

小结:

以上就是用dfs解决的2种常见的问题。综合打“**”的注释,我总结出dfs大致的模版:

void dfs(int step)
{
    if (...) 边界条件
    {
        ...
        return;   //返回上一步(这里一定要写)
    }
    
    for (i=0;i<n;i++)  尝试每一种可能
    {
        if (...)  判断当前可能是否可用
        {
             ...
             book[i] = 1;   //标记这个可能已经尝试
             dfs(step+1);  //继续下一步
             book[i] = 0;   //尝试结束,取消该点的标记(这里一定要写)
        }
    }

    return;
}

其中具体问题具体分析。例如:

“尝试每一种可能”的那个for循环,如果是全排列问题,对于每一个位所填入的数字有 1~9 这9种情况,所以用一个for循环遍历1~9即可;但是如果是对于迷宫问题,对于每一步有4个方向可以走,所以除了要用for循环(0~3),还要用一个二维的“方向数组”dx,dy来辅助才能把4个方向这4种情况表现出来。

“判断当前可能是否可用”的那个if判断中,除了判断标记数组book之外,在迷宫问题中还要注意判断“该点的坐标是否越界”、“该点是否是障碍”等问题。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值