埃及分数问题-迭代加深搜索与IDA*算法
来源:互联网 发布:重庆博拉网络 编辑:程序博客网 时间:2024/05/23 01:19
本文转载自埃及分数问题浅谈对迭代加深搜索的理解
迭代加深搜索一般用来求解状态树非常深,甚至深度可能趋于无穷,但是目标状态浅的问题。如果用普通的DFS去求解,往往效率不够高。此时我们可以对DFS进行一些改进。最直观的一种办法是增加一个搜索的最大深度限制maxd,一般是从1开始。每次搜索都要在maxd深度之内进行,如果没有找到解,就继续增大maxd,直到成功找到解,然后break。如下图所示,DFS遍历9个结点才能找到结点3;迭代加深搜索遍历3个结点就可以找到结点3。
在使用迭代加深搜索时,通常还要引入一个估价函数h()来预测从当前深度还有至少多少步才能到达目标状态。假设当前在第cur层,当cur+h(cur)>maxd时候,就说明不论怎么走,都不可能在maxd的限制之内找到目标状态,此时就可以进行剪枝操作。这样带有估价函数的迭代加深搜索就是IDA*算法。为了更好的理解该算法,下面以埃及分数这一经典的例子来作为说明。在古埃及,人们使用单位分数的和(即1/a,a是自然数)来表示一切有理数,例如,2/3=1/2+1/6,但是不允许2/3=1/3+1/3,因为在加数中不允许有相同的。对于一个分数a/b,表示的方法有很多种,其中加数少的比加数多的好,如果加数个数相同,那么最小的分数越大越好;如果最小的分数也相同,那么次小的分数越大越好,以此类推。输入两个整数a,b(0<a<b<500),试编程计算最佳表达式。
样例输入:
495 499
样例输出:
2 5 6 8 3992 14970
首先,根据题意可以发现,本题的解答树非常的庞大,不仅深度没有明显的上界,而且在理论上加数的选择也是无限的,即每一层的结点数量也是无限的。这样的话,如果使用普通的BFS,第一层都扩展不完。然而本题要实现两个目标:加数的个数尽量少;最小的那个分数尽量大,即最大的分母尽量小。为了实现第一个目标,我们自然想到可以逐一枚举可能的个数,设maxd表示一共有maxd+1个分数相加恰好等于a/b(下标从0开始),按照这样的思路,一定可以找到加数最少的解。接下来考虑如何实现第二个目标。第二个目标其实是在告诉我们如果存在多解的时候,要更新当前找到的最优解。这里容易犯的一个错误是:找到了解之后立即全部return true。这样的写法只能实现目标1,并没有真正实现目标2。正确的做法应该是找到了一组解后,继续返回上一层继续新的搜索。假设当前已经在第cur层,即0~cur的所有的分母都已经确定了,剩下的分数是aa/bb。再假设我们此时需要从分母i>=from的数开始寻找,不难发现,如果(maxd+1-d)*(1/i)≤aa/bb,即剩下的分数全部都为1/i时候,它们的和才刚好等于aa/bb。而实际情况是分母不允许重复出现,即实际的和肯定小于(maxd+1-d)*(1/i),这个时候如果i继续增加,(maxd+1-d)*(1/i)只会更小于aa/bb,情况更糟糕,因此在此处应该停止枚举。这样,我们就分析出来了正确的停止搜索的条件:当出现(maxd+1-d)*(1/i)≤aa/bb时break。通过以上分析,我们还确定出来了dfs时候的入口参数:cur表示当前在第cur层,from表示第cur层分母的起点, aa表示剩下的分数的分子,bb表示剩下的分数的分母。
参考代码:
#include<cstdio>#include<cstring>#include<iostream> #include<algorithm>using namespace std; typedef long long ll; const int N=1000; int maxd; int ans[N],bestans[N]; //找到第一个小于x/y的单位分数的分母 int get_first(int x,int y) { int res=y/x; return res*x>=y?res:res+1; } //求a和b的最大公约数 ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b); } //更新当前的解bool update(int d) { for(int i=d;i>=0;i--){if(ans[i]!=bestans[i]) return bestans[i]==-1||ans[i]<bestans[i]; }//由于分母是由小到大存储因此逆序枚举 return false; } bool dfs(int d,int from,ll aa,ll bb) { if(d==maxd) { if(bb%aa) return false;//如果不能整除搜索失败 ans[d]=bb/aa; if(update(d)) memcpy(bestans,ans,sizeof(ll)*(d+1)); return true; } bool ok=false; from=max(from,get_first(aa,bb));//假设第d-1个分数的分母是a,第d个分数的分母不一定要从a+1开始,还要考虑1/(a+1)是否小于等于aa/bb for(int i=from;;i++) { if(bb*(maxd+1-d)<=i*aa) break; ans[d]=i;//枚举新的分母 ll b2=bb*i; ll a2=aa*i-bb; //计算aa/bb-1/i的分子分母 ll g=gcd(a2,b2);//计算最大公约数进行约分 if(dfs(d+1,i+1,a2/g,b2/g)) ok=true;//不要写成return true,因为找到一组解并不能当做停止枚举的条件 } return ok; } int main() { int a,b; while(~scanf("%d%d",&a,&b)) { int ok=0; for(maxd=1;;maxd++)//迭代加深搜索的主体框架,maxd要设置为全局变量 { memset(bestans,-1,sizeof(bestans)); if(dfs(0,get_first(a,b),a,b)) { ok=1; break; } } for(int i=0;i<=maxd;i++) printf("%d ",bestans[i]); printf("\n"); } }
- 埃及分数问题-迭代加深搜索与IDA*算法
- 埃及分数 迭代加深搜索 IDA*
- 埃及分数问题(迭代加深搜索)
- 迭代加深搜索与埃及分数
- 埃及分数问题 - 迭代加深搜索经典问题
- 埃及分数问题(迭代加深搜索)
- 埃及分数问题 迭代加深搜索(IDDFS)
- 迭代加深搜索与埃及分数求解
- 埃及分数 迭代加深搜索 IDS
- 迭代加深搜索 埃及分数
- 埃及分数,迭代加深搜索
- 埃及分数(迭代加深搜索)
- 埃及分数(迭代加深搜索)
- 埃及分数题解[迭代加深搜索]
- 埃及分数(迭代加深搜索)
- 迭代加深搜索(埃及分数)
- 埃及分数(迭代加深搜索)
- 迭代加深搜索--埃及分数
- 解析动态联编(上篇)
- HashMap和HashSet解析
- Android自定义Toast的使用
- iOS开发之多线程NSThread,NSOperation,GCD
- 解读VC++编程中的文件操作API和CFile类
- 埃及分数问题-迭代加深搜索与IDA*算法
- (转载)NumPy详细教程
- 接口、抽象、委托 --------面向对象相关的一些概念
- Java 不可变类的整洁之道
- 汉字拼音的一个解决方法(初具使用价值)
- kindle我的剪帖文件整理脚本
- nginx之动态数组
- android studio中.9图片的用法。
- 关于回调的一些东东