搜索专题小结:迭代加深搜索

来源:互联网 发布:centos如何启用双网卡 编辑:程序博客网 时间:2024/05/21 09:04

迭代加深搜索

迭代加深搜索(Iterative Deepening Depth-First Search, IDDFS)经常用于理论上解答树深度上没有上界的问题,这类问题通常要求出满足某些条件时的解即可。比如在“埃及分数”问题中要求将一个分数a/b分解成为若干个形如1/d的加数之和,而且加数越少越好,如果加数个数相同,那么最小的分数越大越好。下面总结一下该方法的一般流程:

(1)概述:迭代加深搜索是通过限制每次dfs的最大深度进行的搜索。令maxd表示最大的搜索深度,那么dfs就只能在0~maxd之间来进行,如果在这个范围内找到了解,就退出大循环,否则maxd++,扩大搜索范围。但可想而知,倘若没有高效及时的退出无解的情况,那么时间上的开销也是会比较大的。这时就需要进行“剪枝”操作,及时地判断此时能否找到解。对于迭代加深搜索,经常通过设计合适的“乐观估价函数”来判断能否剪枝。设当前搜索的深度是cur,乐观估价函数是h(),那么当cur+h()>maxd时就需要剪枝。


那么什么是乐观估价函数呢?简单的说就是从当前深度到找到最终的解“至少”还需要多少步,或者距离找到最终的解还需要扩展多少层。如果超出了当前限制的深度maxd,说明当前限制的最大深度下是不可能找到解的,直接退出。比如像前面的“埃及分数”问题,要拆分19/45这样的一个分数,假设当前搜索到了第三层,得到19/45=1/5+1/100...那么根据题意此时最大的分数为1/101,而且如果需要凑够19/45,需要(19/45-1/5-1/100)*101=23个1/101才行。即从第3层还要向下扩展至少大于23层的深度才可能找到所有的解。所以如果此时的maxd<23,就可以直接剪枝了。因此(a/b-c/d)/(1/e)便是本题的乐观估价函数。


注意,使用迭代加深搜索时要保证一定可以找到解,否则会无限循环下去。

下面给出“埃及分数”问题的代码以便更好地理解迭代加深搜索的过程:

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<algorithm>#include<string>#include<sstream>#include<set>#include<vector>#include<stack>#include<map>#include<queue>#include<cstdlib>#include<cstdio>#include<cstring>#include<cmath>using namespace std;typedef long long LL;int maxd;int a, b;const int maxn = 1000;int ans[maxn], v[maxn];int gcd(int a, int b){return b == 0 ? a : gcd(b, a%b);}int get_first(int a, int b)//找到1/c≤a/b时最小的c{int c = 1;while (b > a*c)c++;return c;}bool better(int d)//比较深度为d时,现在找到的解是不是更优的{for (int i = d; i >= 0; i--)if (v[i] != ans[i]){return ans[i] == -1 || v[i] < ans[i];//两种情况下说明当前更优:(1)此时尚未找到过解;(2)当前的分母小于原来的分母,说明当前的分数比原来的更大,符合题意要求}return false;}bool dfs(int d, int from, LL aa, LL bb)//当前深度为d,分母不能小于from,分数之和恰好是aa/bb{if (d == maxd)//到达了最后一层{if (bb%aa)return false;//不能整除,说明最后一项不符合埃及分数的定义,失败退出v[d] = bb / aa;if (better(d))memcpy(ans, v, sizeof(LL)*(d + 1));//当前找到的解是更优的,更新ansreturn true;}bool ok = false;from = max(from, get_first(aa, bb));//更新fromfor (int i = from;; i++)//枚举分母{if (bb*(maxd + 1 - d) <= i*aa)break;//利用乐观估价函数来剪枝,从当前深度d到达maxd一共有maxd-d+1项,如果(maxd-d+1)*(1/i)还凑不够aa/bb,需要剪枝v[d] = i;LL b2 = bb*i;//计算aa/bb-1/i,通分后,分母是bb*i,分母是aa*i-bbLL a2 = aa*i - bb;LL g = gcd(a2, b2);//计算分子,分母的最大公约数,便于约分if (dfs(d + 1, i + 1, a2 / g, b2 / g))ok = true;}return ok;}int main(){int ok = 1;while (scanf("%d%d", &a, &b))//输入分数a/b{for (maxd = 1;; maxd++){memset(ans, -1, sizeof(ans));if (dfs(0, get_first(a, b), a, b)){ ok = 1; break; }}printf("%d/%d=", a, b);for (int i = 0;; i++)if (ans[i]>0)printf("%s1/%d", i == 0 ? "" : "+", ans[i]);else { printf("\n"); break; }}return 0;}

1 0