秒懂百科,C++如此简单丨第十四天:DFS深度优先算法

目录

Everyday English

DFS是什么?

DFS框架

例题详解

1、全排列问题(洛谷 P1706 C++) 

题目概述

思路点拨

参考代码

2、自然数的拆分问题(洛谷 P2404 C++)

题目概述

​编辑思路点拨

AC代码

结尾

Thank you for listening!


 必看信息 

▶本篇文章由爱编程的小芒果原创,首发于CSDN,未经许可,严禁转载。

▶本篇文章被收录于秒懂百科,C++如此简单专栏,欢迎订阅。

☆专栏亮点☆

1.每篇文章质量高,质量分保证在80分以上。

2.文章的内容清晰有条理,图文并茂,附有源代码。

3.每个知识点讲解详细,会有很多补充扩展。

4.若哪个知识点没有懂,可以私信我,我会尽可能地帮助你。

Everyday English

Failure is the mother of success.

失败乃成功之母。

DFS是什么?

DFS是英文Depth-First-Search的缩写,意思是深度优先搜索。

什么是深度优先搜索呢?顾名思义,就是往深处遍历。举个小例子:

假设你现在要挖宝藏,你肯定会往下挖对吧。当你挖到地下10米时,探宝器出现了一个故障。一会儿显示往右下挖,一会儿显示往左下挖。你只好先往左下挖。挖啊挖,不幸的是你挖到了岩石,不能在往下挖了。你只好往回爬,爬到那个分叉口,这次你往右下挖,果然一路顺畅,挖到了宝藏!

这是你挖宝的路线:                                   这是dfs的路线: 

            

      

 这下你应该懂dfs的含义了吧。

对了你爬回去的动作,在dfs里叫作回溯。

那dfs有没有框架呢?有是有的,不像贪心没什么框架(有兴趣的可以看一看我博客里的贪心算法讲解)

DFS框架

void dfs(需要啥参数自己定)
{
	if(终止条件)
	{
		cout<<输出答案<<endl; //当然也可以计数等等 
		return 0;
	}
	else
	{
		for(遍历所有可能性)
		{
			if(条件可行)//满足条件时,再去深搜 
			{
				执行操作;
				dfs(变化后的新参数); 
				撤销操作;//回溯 
			 } 
		}
	}
}

话不多说,上例题,边看题边理解!

例题详解

1、全排列问题(洛谷 P1706 C++) 

题目概述

我也有一篇博客是专门讲这道题的,但是那个要VIP,这篇不用,而且内容一样,还不快点个赞!

题目描述:

按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

输入格式:

一个整数 n。

输出格式:

由 1∼n 组成的所有不重复的数字序列,每行一个序列。

每个数字保留 5个场宽。

输入输出样例:

输入:3                       

输出:     1     2     3

                1     3     2

                2     1     3

                2     3     1

                3     1     2

                3     2     1

思路点拨

这题是一道用dfs解决的经典问题。

我们可以这样想,有n个数,那么就会有n个空位要填进去数

那么终止条件就应该是当已经填的空位数等于n时

在dfs中,从1到n循环依次判断能否放入空位中。

1:若能,将此数存放置数组内并递归下一个空位,也就是dfs(x+1)

2:若不能,则接着判断下一个数

而此处的判断条件应该是此数是否出现过,因为全排列不能出现相同的数

我们可以定一个vis[]数组来判断此数是否出现过(开始全部为false)。

判断时,只需看看这个位置的数组元素是true还是false。

如果是执行‘思路’正数第四行,如果是执行‘思路’正数第五行。

最后一定要记得把vis[]设为false(也就是回溯)!

参考代码

#include<bits/stdc++.h>
using namespace std;
bool vis[10];
int n,a[100]; 
void dfs(int k)
{
	//当已经填好的空位数=n时,按格式输出a[i]并终止 
	if(k==n)
	{
		for(int i=1;i<=n;i++) cout<<setw(5)<<a[i];
		cout<<endl;
		return;
	}
	//判断 + 填数 
	for(int j=1;j<=n;j++)
	{
		if(!vis[j]) 
		{
			a[k+1]=j;
			vis[j]=true;//不能再用这个数了 
			dfs(k+1);//这个位置已经填好,递归填下一个 
			vis[j]=false;//回溯 
		}
	}
	
}
int main()
{
	cin>>n;
	dfs(0);//表示已经填好的空位数 
	return 0;
}

2、自然数的拆分问题(洛谷 P2404 C++)

题目概述

思路点拨

看到这题,首先就会想到用深搜。对,这道题的正解的确是深搜

可搜什么呢?假如你看到一道题给出的数字是5,让你拆分,你会怎么分呢?

相信大家的思路都是这样的:

首先确定第一个数字,从小的开始,也就是说第一个数字可以是1。

接下来,我们发现,1后面数字相加的和,只可能是5-1=4。

紧接着,我们把4来拆分,还是从1开始拆,那么现在的算式就变成了1+1。

那后面拆分的总和应该是5-2/4-1=3,以此类推,最终拆成1+1+1+1+1。

那如果第一个数拆2呢,那么剩下的数相加得5-2=3。

再把3拆分,最后得出结果。

这是我们人的思路,那程序怎么写呢?

我们可以dfs(cur,sum),表示当前考虑cur,还剩sum需要划分。

注意:在dfs里因该有两种情况,分别是选或不选。

最后奉上完整代码。

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,t=0,a[10000];
void dfs(int cur,int sum)//考虑cur,还剩下sum需要划分 
{
    //当没有余下的(没有需要划分时),输出答案 
    if(sum==0)
    {
        cout<<a[0];
        for(int i=1;i<t;i++) cout<<"+"<<a[i];
        cout<<endl;
        return;
    }
    //如果要考虑的这个数比剩下要划分的大,没办法选,return
    //如果考虑的这个数等于输入的数,不成立,因为拆分至少有两个数,return  
    if(cur>sum||cur==n) return;
    a[t++]=cur;//把考虑的数加入答案数组 
    dfs(cur,sum-cur);//选cur 
    t--;//回溯 
    dfs(cur+1,sum);//不选cur 
}
int main()
{
    cin>>n;
    dfs(1,n); 
    return 0;
}

结尾

你学会了吗?都看到这里了,还不给我一个免费的一键三连?

我还是一名小学生希望多多支持一下,谢谢!

Thank you for listening!

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的小芒果

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值