习题7-7 Egyptian Fractions (HARD version) 埃及分数 (IDA*)

本文介绍了一种在特定限制条件下求解埃及分数的方法。通过迭代加深搜索的方式,并结合剪枝优化,实现对指定分母集合的规避,最终输出满足条件的埃及分数表示。

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

大体题意:

在普通的埃及分数上加了  几个限制, 多给了你K个数,让你求出最后的埃及分数结果中分母不能是K个数的某个数的结果。

思路:

先记录一下 埃及分数算法:

利用了迭代加深的搜索方式

最后的分母结果记录在ans数组中,最后只需要输出ans 数组即可!

首先将ans数组初始化为-1,表示没赋过值,这样在更新答案时,可以判断ans[i] == 1或者  v[i] < ans[i]  这样可以更新ans数组!

在dfs 函数中,有一个深度d  到达maxd时 进行判断返回,还有一个int from 参数,表示枚举的分母从哪里开始,然后就是目标分子和分母了!

然后d 到达maxd时,判断目标分数是不是埃及分数 和此时是不是更优解。  如果不是埃及分数直接return false了,最后return true;

如果没到达maxd,则先更新一下枚举分母的起源,然后就是剪枝,都合法时算出下一个分数,在进行dfs即可!



该题目多的限制 要在两个地方进行判断,一个是dfs 没到达maxd时在枚举分母时,发现i 时禁止的 直接continue;

另一个地方时 到达maxd时要判断更新的ans 是不是禁止的!、


具体详见代码和书籍!

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fin freopen("cin.txt","r",stdin);
#define fout freopen("out.txt","w",stdout);
using namespace std;
typedef long long ll;
const int maxn = 10000 + 10;
int maxd,k;
int Not[maxn];
ll v[maxn],ans[maxn];
int get_first(int a,int b){
    return b/a+1;
}
ll gcd(ll a,ll b){
    return !b?a:gcd(b,a%b);
}
bool forbid(int n){
    for (int i = 0; i < k; ++i)if (Not[i] == n)return true;
    return false;
}
bool better(int d){
    for (int i = d; i >= 0; --i) if (v[i] != ans[i])
        return ans[i] == -1 || v[i] < ans[i];
    return false;
}
bool dfs(int d,int from, ll aa, ll bb){
    if (d == maxd){
        if (bb % aa)return false;
        v[d] = bb/aa;
        if (forbid(bb/aa))return false;
        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){
        if (forbid(i))continue;
        if (bb * (maxd-d+1) <= i * aa)break;
        v[d] = i;
        ll b2 = i*bb;
        ll a2 = i*aa-bb;
        ll g = gcd(a2,b2);
        if (dfs(d+1,i+1,a2/g,b2/g))ok=1;
    }
    return ok;
}
int main(){
//    fin;fout;
    int T,cnt = 0;
    scanf("%d",&T);
    while(T--){
        int a,b;
        scanf("%d%d%d",&a,&b,&k);
        memset(ans,-1,sizeof ans);
        for (int i = 0; i < k; ++i)scanf("%d",&Not[i]);
        int ok = 0;
        for (maxd = 1;;++maxd){
            if (dfs(0,get_first(a,b),a,b)){
                ok = 1;
                break;
            }
        }
        printf("Case %d: %d/%d=",++cnt,a,b);
        for (int i = 0; i <= maxd; ++i){
            if (i)printf("+");
            printf("1/%lld",ans[i]);
        }
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值