由“埃及分数”引入迭代加深搜索

来源:互联网 发布:java简单售票系统 编辑:程序博客网 时间:2024/06/05 22:44

迭代加深搜索(IDDFS)的思想

迭代加深搜索一般用来求解状态树“非常深”,甚至深度可能趋于无穷,但是“目标状态浅”的问题。如果用普通的DFS去求解,往往效率不够高。此时我们可以对DFS进行一些改进。最直观的一种办法是增加一个搜索的最大深度限制dep,一般是从1开始。每次搜索都要在dep深度之内进行,如果没有找到解,就继续增大dep,直到成功找到解,然后break。


如下图所示,如果用DFS,需要15步才能找到结点3,但是用迭代加深搜索,很快即可找到结点3。

迭代加深搜索

为了更好的理解该算法,下面以“埃及分数”这一经典的例子来作为说明。


埃及分数问题

在古埃及,人们使用单位分数的和(即1/a,a是自然数)来表示一切有理数,

例如,2/3=1/2+1/6,但是不允许2/3=1/3+1/3,因为在加数中不允许有相同的。

对于一个分数a/b,表示的方法有很多种,其中加数少的比加数多的好,如果加数个数相同,那么最小的分数越大越好。

例如,19/45=1/5+1/6+1/18是最优方案。

输入两个整数a,b(0<a<b<1000),试编程计算最佳表达式。

样例输入:

19 45

样例输出:

5 6 18


问题分析


本题由于搜索层数不明,所以考虑迭代加深搜索。

确定了搜索模式后,易得到以下两个基本思路:

1.枚举对象:分母。因为a/b = 1/a[1] + 1/a[2] + 1/a[3] + ...... + 1/a[n],所以不妨设a[1] < a[2] < a[3] < ..... < a[n]。

2.剪枝手段:定分母上下界。

设限定搜索层数为dep,当前搜索到第k层,当前正要枚举分母a[k],还需枚举总和为x/y的分数。

answer[i]表示当前最优解中的第i个分母,如果还没有解则表示正无穷。 则,


下界:max(y/x,a[k-1]) +1 <= a[k]

[两种情况(下界越大越好):

1.因为必然a[k-1] < a[k],所以a[k-1] + 1 <= a[k]。

2.如果x/y正好等于现要求的分数(如1/18),则y/x便是分母(a[k]是分母)。]


上界:a[k] <= min(y/x * (dep-k+1),anwser[dep]-1,maxlongint/x)

[三种情况(上界越小越好):

1.(平分)因为剩余层数为(dep-k+1),还需枚举总和为x/y的分数,

则可列出式子 (dep-k+1) * 1/a[k] = x/y,求得a[k]最大 = y/x * (dep-k+1)。

2.当有解时(作标记),a[k] <= anwser[dep]-1。

3.最大范围确定一个解,maxlongint/x,防止溢出。]


代码

#include <bits/stdc++.h>using namespace std;const long long INF = 0x7fffffff / 2;bool flag; //标记是否有解 long long d[20], answer[20];long long gcd(long long a, long long b) {    if(b == 0) return a;    return gcd(b, a % b);}void dfs(long long x, long long y, int k) { //决定第k个分母d[k]的值,x分子,y分母     if(k == dep + 1) return;    else if(x == 1 && y > d[k - 1]) { //此时x/y不必再分解,单独作为其中一个解         d[k] = y;        if(!flag || d[k] < answer[k]) //为有解或当前最优解较优             memcpy(answer, d, sizeof(d));        flag = 1; //标记已经有解(可行解)         return ;    }    else {        long long s, t, m;        s = max(y / x, d[k - 1]) + 1; //确定d[k]的上下界,s,t         t = (dep - k + 1) * y / x;        if(t > INF) t = INF; //防止溢出         if(flag && t > d[dep] - 1) t = d[dep] - 1; //上界为已求最优解 - 1         for(long long i = s; i <= t; i ++) {            d[k] = i;            m = gcd(x * i - y, y * i); // a/b - 1/i = x * i - y / y * i             dfs((x * i - y) / m, (y * i) / m), k + 1); //继续搜索         }    }}int main() {    long long A, B;    scanf("%lld%lld", &A, &B);    long long k = gcd(A, B);    A/=k; B/=k;    flag = 0; d[0] = 1;    for(int dep = 1; dep < 10; dep ++) { //枚举得到最大深度         dfs(A, B, 1);        if(flag) {            for(int i = 1; i <= dep; i ++)                printf("%d ", answer[i]);            break;        }    }}


0 0