深度优先搜索

162 阅读2分钟

从今天开始要复习学过的算法,把之前学过的知识串一起,今天现在看深度优先搜索,以下简称深搜,所谓深搜,其实算是递归的一种的特殊方法——回溯,就是一直往下走,直到走不了,再回头,这样说可能有点抽象了,来结合实例看一看。

题目是loj一本通的10018题,大致题意是给你个数字n,把他分成m个正整数之和,问一共有几种分法,其实这个题就是个dfs问题,我的思路是一直找,直到找到m个数,但是由于可能有重复的情况,所以处理一下找的下个数字,一定要大于等于上一个数字,这样既减少了计算次数,又不用判断是否重复,同时注意为了保证当前的数以后的数都大于等于它,截止条件是n-前面的和再比上后面还剩的数的个数

[题目链接](#10018. 「一本通 1.3 例 1」数的划分 - 题目 - LibreOJ (loj.ac))

#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<math.h>
#include<string.h>
using namespace std;
typedef long long ll;
const ll maxn=1e6+10;
int n,m;
int a[210];
int ans=0;///记录组数
void dfs(int k)///k表示找到m个数中的第k个
{
	if (n==0) ///如果n=0,不管m为几,ans都=0
		return ;
	if (k==m)///如果 找到第m个了
	{
		if (n>=a[k-1])///n为分到第k个剩的数,只有它大于前一个数,才不会和其他情况重复
		{
			ans++;
		}
		return ;
	}
	for (int i=a[k-1]; i<=n/(m-k+1); i++) ///起止条件是确保求出来的序列是非递减的
	{
		a[k]=i;///找到第k个数字
		n-=i;///n-i表示下面m-k个数还剩n-i
		dfs(k+1);///找下一个数
		n+=i;///加回来是为了找下一组不同的情况
	}
}
int main()
{
	cin>>n>>m;
	a[0]=1;///定义a[0]=1,k=1是必须>=1;
	dfs(1);///从第1个数开始找
	cout<<ans<<endl;
}


这个dfs算比较简单的,我们再看一种迭代加深,同时是优化了搜索顺序,减少了时间复杂度 #10021. 「一本通 1.3 例 4」Addition Chains - 题目 - LibreOJ (loj.ac) 这题同样是要找到ans个数,ans要最小,而且要输出,整体思想和上一题差不多,但是我们把上一题的m个数,换成m层,同时因为是最小的ans的值,所以我们再找下一层的值的时候,优化了搜索顺序,从大到小的遍历 `

#include<bits/stdc++.h>
using namespace std;
int n,a[1000010],book[1000010];
int minn;
//a数组表示我们在递归当中记录的值
//minn表示我们搜索到的最小层数
//book表示我们输出的最后答案 
void dfs(int d)
{
	if (d-1>minn)///r如果搜索到的前一层已经比定义的层的大了,就返回 
		return ;
	if (a[d-1]>n)///如果搜索到的前一层的值已经大于n了,返回 
		return ;
	if (a[d-1]==n)///如果上一层的值等于n 
	{
		if (d-1>minn)//如果我们找到的层数的前一层大于我们搜索到的最小层数,返回 
			return ;
		minn=d-1;///更新最小层数 
		for (int i=1;i<=n;i++)
			book[i]=a[i];///传值 
	}
	else///上一层的值小于n 
	{
		for (int i=d-1;i>=1;i--)	///优化搜索顺序 
		{
			if (a[d-1]+a[i]<=n)///确保任意两个值相加<=n 
			{
				a[d]=a[d-1]+a[i];///更新当前层的值 
				dfs(d+1);///寻找下一层 
				a[d]=0;///清零,接着进行下一个i 
			}
		}
	}
}
int main()
{
	while(1)
	{
		cin>>n;
		if (n==0)
			break;
		minn=999999;
		a[1]=1;
		dfs(2);
		for (int i=1;i<=minn;i++)
			cout<<book[i]<<" ";
		cout<<endl;
	}
	return 0;
}`

其实这两道题还都用上了剪枝,如果这两道题都理解了,那咱们再回头去看迷宫的深搜问题,就很简单了。