一、DFS的缺陷
我们知道DFS是一条路走到黑,直到将整条路走完以后才会回头。
这就导致了一个问题,路很多,你没有办法确定当下走的路就是正确的,同时,如果这条路是错误的,却又无比的长。这就会导致你在错误的路上越走越远。
示意图如下图所示:
红色点是答案,而如果我们不幸地从左侧的最长的方案开始搜索的话,就会导致我们在错误的路上越走越远。那么为了解决这类极端的情况,我们就需要使用迭代加深的算法。
二、迭代加深
1、什么是迭代加深
迭代加深就是我们规定一个DFS的深度,如果到了该深度还没有找到答案的话,我们就及时收手,换一条路搜索,如果在该深度下,没有任何一条路是答案的话,我们就增加DFS的深度,再从头开始来一遍。
很明显这样做的话,我们就解决了在一条错误地路上越走越远的困境,但是很明显,在每个深度中,它都在不断地从头尝试每一种方案,这就造成了很多重复的步骤,那么这种重复的现象对我们时间复杂度有什么影响呢?
我们看下面的分析。
2、复杂度分析
我们这里分析的主要是刚才的问题,重复的搜索是否会影响到该算法的效率。我们看一下最坏情况下,假设我们每次只有两个选择,即整个搜索树是一个满二叉树,此时我们这个算法的时间复杂度是怎样的。
假设我们遍历了每一个分支,那么在遍历最后一排的时候,我们会重复遍历最后一排上面的所有节点,但是即使是在这种情况下,我们重复遍历一遍的节点数也只是接近于我们最后一排的节点数。
也就是说在最坏条件下,影响也没有那么大。
3、算法步骤
这个算法的过程还是比较简单的,就是我们把搜索深度设置为1,然后开始搜索,如果当前深度没有搜到答案的话,我们就递增深度。直到搜索到答案,这个过程可以写成一个while循环。
三、例题
1、问题
2、分析
这道题的话,需要满足几个条件:
1、第一个元素和最后一个元素是固定的。
2、中间的元素是严格单调递增的。
3、当前元素等于前面某两个的和
那么最坏条件下, 就是从1,2,3,4,5…开始逐渐递增,那么到达最大值的话,大概是100层。
最好条件下,就是指数级别的增长,1,2,4,8,16,32,64,128。最多8层。
我们发现最好情况和最坏情况之间差了90多层。面对这种极端情况,我们就需要使用我们的迭代加深了。
3、代码
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 110;
int path[N];
int n;
bool dfs(int u, int deep)
{
if(u > deep)return false;
if(path[u - 1] == n)return true;
bool st[N] = {0};
for(int i = u - 1; i >= 0; i -- )
for(int j = i; j >= 0; j -- )
{
int x = path[i] + path[j];
if(x > n || x <= path[u - 1] || st[x])continue;
st[x] = true;
path[u] = x;
if(dfs(u + 1, deep))return true;
}
return false;
}
void solve()
{
while(cin >> n, n)
{
path[0] = 1;
int deep = 1;
while(!dfs(1, deep))deep ++;
for(int i = 0; i < deep; i ++ )
cout << path[i] << " ";
cout << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
}