codevs1288埃及分数-迭代加深搜索
2017-12-05 08:55
288 查看
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)便是本题的乐观估价函数。
注意,使用迭代加深搜索时要保证一定可以找到解,否则会无限循环下去。
练习:
迭代加深搜索(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)便是本题的乐观估价函数。
注意,使用迭代加深搜索时要保证一定可以找到解,否则会无限循环下去。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long LL; const int maxn = 1000; LL ans[maxn],v[maxn]; int maxd,kcase; int gcd(LL a,LL b) { //最大公约数,用于约分 if(!b)return a; return gcd(b,a%b); } LL get_first(LL a, LL b) { //满足1/c<=a/b的最小c return b/a + 1; } bool better(int d) { //更新最优值:如果当前解v比目前最优解ans更优,更新ans return v[d]<ans[d]; } //当前深度为d,分母不能小于from,分数之和恰好为aa/bb bool dfs(int d,int from,LL aa,LL bb) { if(d == maxd) { //深度达到当前枚举个数 if(bb % aa) return false;//aa/bb必须是埃及分数 v[d] = bb/aa;//保存分母 if(better(d)) { memcpy(ans,v,sizeof(LL)*(d+1)); //更新最优解 } return true;//返回成功 } bool ok = false;//用于返回本次递归的结果 from = max((LL)from,get_first(aa,bb));//枚举的起点 for(int i=from; ; i++) { if(bb * (maxd+1-d) <= i*aa) break;//剪枝:如果剩下的maxd+1-d个分数全部都是1/i,加起来仍然不超过aa/bb,则无解! (maxd+1-d)/i <= aa/bb v[d] = i;//保存分母 //计算aa/bb - 1/i,设结果为a2/b2 LL b2 = bb*i; LL a2 = aa*i - bb; LL g = gcd(a2,b2);//用于约分 if(dfs(d+1,i+1,a2/g,b2/g)) ok = true;//找到解返回成功 } return ok; } void solve(int a,int b) { int ok = 0; for(maxd = 1; ; maxd++) { //枚举深度,即等式相加的个数 memset(ans,127,sizeof(ans)); if(dfs(0,get_first(a,b),a,b)) { //递归找到时返回成功 ok = 1; break;//标记并退出枚举 } } if(ok) { for(int i=0; i<=maxd; i++) printf("%lld ",ans[i]); } else printf("%d/%d\n",a,b); } int main() { int a,b; scanf("%d%d",&a,&b) solve(a,b); return 0; }
练习:
【POJ2286】The Rotation Game
相关文章推荐
- [CODEVS1288]埃及分数(迭代加深搜索)
- [迭代加深搜索] Codevs1288 埃及分数问题
- [codevs1288]埃及分数(迭代加深搜索)
- [codevs1288]埃及分数 迭代加深搜索
- codevs 1288 埃及分数 迭代加深搜索
- Codevs 1288 埃及分数(迭代加深搜索)
- [codevs 1288] 埃及分数 [IDdfs 迭代加深搜索 ]
- 搜索+剪枝——CODEVS1288 埃及分数
- 埃及分数 迭代加深搜索 IDA*
- 小白书埃及分数之加深迭代搜索
- 【算法学习笔记】17.暴力求解法05 隐式图搜索1 迭代加深搜索 埃及分数
- 埃及分数(codevs 1288)
- 埃及分数(迭代加深搜索)
- 埃及分数 迭代加深搜索 IDA*
- 【算法学习笔记】17.暴力求解法05 隐式图搜索1 迭代加深搜索 埃及分数
- 【基础练习】【IDA*】codevs1288 埃及分数题解
- “埃及分数”问题浅谈对迭代加深搜索的理解
- 迭代加深搜索-DFSID:埃及分数
- 章七 埃及分数(迭代加深搜索)
- 埃及分数 迭代加深搜索 IDA*