• 埃及分数问题+迭代加深搜索


    • 理论上可以用回溯法求解,但是解答树非常恐怖,其一深度没有明显上限,1/i的值似乎可以在枚举不断更大的i时越来越小;其二加数的选择在理论上无限制。
    • 解决方案采用迭代加深搜索:从小到大枚举深度上限maxd,每次只执行深度不超过maxd的结点。这样,if(bb*(maxd+1-d)<=i*aa) break;在i++到一定值后一定会退出循环,不会在已经得到解的情况下继续往下递归
    • 埃及分数这道题题意说加数少的比加数多的好,所以在主程序当前maxd下,得到了解ok=1,则不管接下来的maxd,程序结束
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cassert>
    using namespace std;
    int a, b, maxd;
    typedef long long LL;
    LL gcd(LL a, LL b)  //辗转相除法,求最大公因子
    {
        return b == 0 ? a : gcd(b, a%b);
    }
    // 返回满足1/c <= a/b的最小c
    inline int get_first(LL a, LL b)
    {
        return b/a+1;
    }
    const int maxn = 100 + 5;
    LL v[maxn], ans[maxn];
    // 如果当前解v比目前最优解ans更优,更新ans这一判断的依据
    bool better(int d)  //前d个,这时d只能是maxd,
    {
        for(int i = d; i >= 0; i--) if(v[i] != ans[i])
            {
                return ans[i] == -1 || v[i] < ans[i];//没被访问过,或者是当前序列更优
            }
        return false;
    }
    // 当前深度为d,分母(注意是分母)不能小于from,分数之和恰好为aa/bb
    bool dfs(int d, int from, LL aa, LL bb)
    {
        if(d == maxd)
        {
            if(bb % aa) return false; // aa/bb必须是埃及分数(任何数mod1都等于0,可以起到一部分剪枝的作用)
            v[d] = bb/aa;//不用再进一步展开分数
            if(better(d)) memcpy(ans, v, sizeof(LL)*(d+1));
            return true;
        }
        bool ok = false;//最后一层没找到解,可向上返回
        from = max(from, get_first(aa, bb)); // 枚举的起点( 越大,取倒之后就越小)
        for(int i = from; ; i++)  //进行枚举
        {
            // 剪枝:如果剩下的maxd+1-d个分数全部都是1/i,加起来仍然不超过aa/bb,则无解
            if(bb * (maxd+1-d) <= i * aa) break;//从第0层开始算的,总共有maxn+1个
            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, get_first(a2/g, b2/g), a2/g, b2/g)) ok = true;
        }
        return ok;//注意这些返回条件
    }
    
    int main()
    {
        int kase = 0;
        while(cin >> a >> b)
        {
            int ok = 0;
            for(maxd = 1; maxd <= 100; maxd++)  //从小到大,枚举深度上限
            {
                memset(ans, -1, sizeof(ans));
                if(dfs(0, get_first(a, b ), a, b))
                {
                    ok = 1;//找到解了~~
                    break;
                }
            }
            cout << "Case " << ++kase << ": ";
            if(ok)
            {
                cout << a << "/" << b << "=";
                for(int i = 0; i < maxd; i++) cout << "1/" << ans[i] << "+";
                cout << "1/" << ans[maxd] << "
    ";
            }
            else cout << "No solution.
    ";
        }
        return 0;
    }
    

      

    认准了,就去做,不跟风,不动摇
  • 相关阅读:
    第一次团队作业
    第二次结对作业
    动态代理与AOP
    笔试题
    java并发面试题(带答案)
    线程问题——同步和死锁
    java线程的方法
    java实现多线程的方法
    使用java闭锁实现并发
    Java多线程——同步问题
  • 原文地址:https://www.cnblogs.com/mdz-great-world/p/6378386.html
Copyright © 2020-2023  润新知