您的位置:首页 > 其它

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)便是本题的乐观估价函数。

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

#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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: