(算法竞赛入门)埃及分数问题学习笔记

本文介绍了在算法竞赛中遇到的埃及分数问题,通过学习刘汝佳的代码,理解了迭代加深搜索、辗转相除法等技术。文章强调了欧几里得辗转相除法在求最大公因子中的应用,以及如何避免浮点数处理,同时探讨了如何进行有效的剪枝和状态存储。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1.本来以为dfs学的不错了,结果看刘汝佳代码,那些剪枝,返回值,头还是有点大

2.这道题还是有很多细节值得我学习

比如这个欧几里得辗转相除法的函数,一句代码啊,涨知识了

LL gcd(LL a, LL b)  //辗转相除法,求最大公因子
{
    return b == 0 ? a : gcd(b, a%b);
}

3,还有就是这道题,针对分数的一些处理,诸如取倒,通分,避免了浮点数的出现

4.迭代加深搜索这种方法得掌握

#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为dfs时的中间
// 如果当前解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] << "\n";
        }
        else cout << "No solution.\n";
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值