搜索——ID 迭代加深

原文:http://blog.csdn.net/u014800748/article/details/44998693

迭代加深搜索

迭代加深搜索(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)便是本题的乐观估价函数。


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

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

[cpp]  view plain  copy
 print ?
  1. #define _CRT_SECURE_NO_WARNINGS  
  2. #include<iostream>  
  3. #include<algorithm>  
  4. #include<string>  
  5. #include<sstream>  
  6. #include<set>  
  7. #include<vector>  
  8. #include<stack>  
  9. #include<map>  
  10. #include<queue>  
  11. #include<cstdlib>  
  12. #include<cstdio>  
  13. #include<cstring>  
  14. #include<cmath>  
  15. using namespace std;  
  16.   
  17. typedef long long LL;  
  18. int maxd;  
  19. int a, b;  
  20. const int maxn = 1000;  
  21. int ans[maxn], v[maxn];  
  22. int gcd(int a, int b)  
  23. {  
  24.     return b == 0 ? a : gcd(b, a%b);  
  25. }  
  26. int get_first(int a, int b)//找到1/c≤a/b时最小的c  
  27. {  
  28.     int c = 1;  
  29.     while (b > a*c)c++;  
  30.     return c;  
  31. }  
  32. bool better(int d)//比较深度为d时,现在找到的解是不是更优的  
  33. {  
  34.     for (int i = d; i >= 0; i--)  
  35.     if (v[i] != ans[i])  
  36.     {  
  37.         return ans[i] == -1 || v[i] < ans[i];//两种情况下说明当前更优:(1)此时尚未找到过解;(2)当前的分母小于原来的分母,说明当前的分数比原来的更大,符合题意要求  
  38.     }  
  39.     return false;  
  40. }  
  41.   
  42. bool dfs(int d, int from, LL aa, LL bb)//当前深度为d,分母不能小于from,分数之和恰好是aa/bb  
  43. {  
  44.     if (d == maxd)//到达了最后一层  
  45.     {  
  46.         if (bb%aa)return false;//不能整除,说明最后一项不符合埃及分数的定义,失败退出  
  47.         v[d] = bb / aa;  
  48.         if (better(d))memcpy(ans, v, sizeof(LL)*(d + 1));//当前找到的解是更优的,更新ans  
  49.         return true;  
  50.     }  
  51.     bool ok = false;  
  52.     from = max(from, get_first(aa, bb));//更新from  
  53.     for (int i = from;; i++)//枚举分母  
  54.     {  
  55.         if (bb*(maxd + 1 - d) <= i*aa)break;//利用乐观估价函数来剪枝,从当前深度d到达maxd一共有maxd-d+1项,如果(maxd-d+1)*(1/i)还凑不够aa/bb,需要剪枝  
  56.         v[d] = i;  
  57.         LL b2 = bb*i;//计算aa/bb-1/i,通分后,分母是bb*i,分母是aa*i-bb  
  58.         LL a2 = aa*i - bb;  
  59.         LL g = gcd(a2, b2);//计算分子,分母的最大公约数,便于约分  
  60.         if (dfs(d + 1, i + 1, a2 / g, b2 / g))ok = true;  
  61.     }  
  62.     return ok;  
  63. }  
  64.   
  65. int main()  
  66. {  
  67.     int ok = 1;  
  68.     while (scanf("%d%d", &a, &b))//输入分数a/b  
  69.     {  
  70.         for (maxd = 1;; maxd++)  
  71.         {  
  72.             memset(ans, -1, sizeof(ans));  
  73.             if (dfs(0, get_first(a, b), a, b)){ ok = 1; break; }  
  74.         }  
  75.         printf("%d/%d=", a, b);  
  76.         for (int i = 0;; i++)  
  77.         if (ans[i]>0)  
  78.             printf("%s1/%d", i == 0 ? "" : "+", ans[i]);  
  79.         else { printf("\n"); break; }  
  80.     }  
  81.     return 0;  
  82. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值