从今天开始要复习学过的算法,把之前学过的知识串一起,今天现在看深度优先搜索,以下简称深搜,所谓深搜,其实算是递归的一种的特殊方法——回溯,就是一直往下走,直到走不了,再回头,这样说可能有点抽象了,来结合实例看一看。
题目是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;
}`
其实这两道题还都用上了剪枝,如果这两道题都理解了,那咱们再回头去看迷宫的深搜问题,就很简单了。