埃及分数题解[迭代加深搜索]

题目

  • 链接

https://vijos.org/p/1308
http://codevs.cn/problem/1288/

  • 题目描述 Description
    在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数。 如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的。 对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越 好。 如: 19/45=1/3 + 1/12 + 1/180 19/45=1/3 + 1/15 + 1/45 19/45=1/3 + 1/18 + 1/30, 19/45=1/4 + 1/6 + 1/180 19/45=1/5 + 1/6 + 1/18. 最好的是最后一种,因为1/18比1/180,1/45,1/30,1/180都大。 给出a,b( 0<a<b<1000 ),编程计算最好的表达方式。

  • 输入描述 Input Description
    a b

  • 输出描述 Output Description
    若干个数,自小到大排列,依次是单位分数的分母。

  • 样例输入 Sample Input
    19 45

  • 样例输出 Sample Output
    5 6 18

题解

  • 这个搜索近几天是越来越烦人了!
  • 此题一眼望去就是搜索,然而,题目中没给深度,所以用dfs乱搜是不行的。
  • 没给深度,用bfs?
  • 也不好,用bfs不敢剪枝。这个状态还要扩展几十次才能出解。什么,这就是最优方案?我怎么知道?
  • 那用A*吧。
  • 请问估价函数怎么写?请问这么大的空间开销怎么办?
  • 正解:Dfs-ID,迭代加深搜索!一层一层地搜;用dfs的空间开销解决bfs的问题,并且方便了剪枝;有现成的系统栈能调用,还不用打堆;代码量小,易写易调试。
  • 搜索后几层的时候前几层会重搜一遍,效率会降低。
  • 不怕,前几层的代价相对于后一层来说又算得了什么呢?

  • 以把原分数分解成的分数个数K为层数,从一开始的状态按照分母从小到大的顺序搜索。若在某一层搜到了解并且这一层已经搜完了,那这一层的最优解一定是整个问题的最优解。

  • 假设现在要分解的分数是a/b;
  • 若当前原分数已经分解成为超过K个分数,直接返回即可。
  • 若a能整除b,那分解结束了,最后一个分母是b/a。用当前的解去更新答案,如果没有答案或当前解包含的分母个数少于答案或当前解包含的分母个数等于答案且当前解的最大分母比答案大,则当前解即为答案。
  • 否则,要枚举i,使得 ab=ab+1i ,使i作为当前解的一部分,进入下一层递归。
  • i的范围如何确定?首先i要大于等于 [ba] ,只有这样,才能使 ab1i ,这样的i才满足题意;其次i要大于当前解中分母最大的元素,这是搜索顺序决定的。同时i的值不能太大,要满足当前的分数有能分解成K个以内分数的可能性,假设i作为第step个分母,那么第step+1到第K个分母都应大于i,这些分数的和小于 Kstepi ,要有解,则必须 1i+Kstepi=Kstep+1iab ,即 i[b(Kstep+1)a] ,如果算出来的i大于int的最大值,那就以int的最大值为界,否则算一段时间就会爆掉的,体现为变成负数然后TLE。注意 ab 进入下层时要约分,否则爆掉的几率会大大增加。
  • 然后,这个复杂的问题就解决了。

  • Code

/*
3 997
2 34
*/
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 15, oo = 2147483647;
ll A, B, K;
ll ans[N], tmp[N];
bool flag;
inline ll gcd(ll a, ll b)
{
    if(a < b) swap(a, b);
    ll c;
    while(b != 0)
    {
        c = a % b;
        a = b; b = c;
    }
    return a;
}
void dfs(ll stp, ll a, ll b)
{
    if(stp > K) return;
    if(a == 1 && b > tmp[stp - 1])
    {
        tmp[stp] = b;
        if(!flag || tmp[stp] < ans[stp])
        {
            memcpy(ans, tmp, sizeof(tmp));
        }
        flag = true;
        return;
    }
    ll s = max(b / a, tmp[stp - 1] + 1), t = (K - stp + 1) * b / a;
    if(t > oo) t = oo - 1;
    if(flag && t >= ans[K]) t = ans[K] - 1;
    for(ll i = s; i <= t; ++i)
    {
        tmp[stp] = i;
        ll m = gcd(i * a - b, i * b);
        dfs(stp + 1, (i * a - b) / m, (i * b) / m);
    }
}
int main()
{
    scanf("%lld%lld", &A, &B);
    ll C = gcd(A, B);
    A /= C; B /= C;
    if(A == 1)
    {
        printf("%lld\n", B);
        return 0;
    }
    flag = false;
    tmp[0] = 1;
    for(K = 1; ; ++K)
    {
        dfs(1, A, B);
        if(flag)
        {
            for(ll i = 1; i <= K; ++i)
            {
                printf("%lld ", ans[i]);
            }
            break;
        }
    }
    return 0;
}

你可能感兴趣的:(OI党坚毅的步伐,搜索大法好)