UVA12558 Egyptian Fractions (HARD version)(埃及分数)

传送门

题目大意

给出一个真分数 a/b,要求出几个互不相同的埃及分数(从大到小),使得它们之和为 a/b

(埃及分数意思是分子为1的分数,详见百度百科

如果有多组解,则分数数量少的优先

如果分数数量一样则分母最大的要尽量小,如果最大的分母同样大,则第二大的分母尽量小,以此类推

为了加大难度,会给出k个不能作为分母的数

(2<=a,b<=876,k<=5 并且 a,b 互质)

 

首先想的是数论,但是呢

推不出来...

然后发现a,b好像不大

貌似可以搜索

但是呢

不知道上界...

那就迭代加深搜索呗

然后想想怎么剪枝

如果知道 a/b,要怎么求出最小 k 使 1/k < a/b 呢(注意符号)

 

易知 a/b 为真分数 ,a,b,k均为整数
所以 a<b
所以必定有整数 x,y 使得 x*a+y=b 且y<a
所以可得 int(b/a)=int( (x*a+y) /a )=int(x*a/a)+int(y/a) = x
因为 int(b/a)<=b/a,int(b/a)=x
所以 x+1 > b/a , 所以 int(b/a)+1>b/a,
所以1/(x+1) < a/b 又 1/int(b/a)=1/x >=a/b 所以 x+1就是最小的 k 使得 1/k < a/b 所以 k=int(b/a) + 1 证明完毕

 

 

 

知道了 k 的下界,那还要求 k 的上界

显然每次求的分母肯定要比上一次大(题目要求)

所以如果后面所有的分母都为 k ,加起来还没有当前要求的数大,那就不要再搜下去了,再下去也不会有结果的

然后就可以搜了,至于判断是否可用我用的是set

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
using namespace std;
const int N=1e5+7;
set <long long> p;
long long t,A,B,n,mxdep;
long long ans[N],w[N];
bool flag;
inline long long gcd(long long a,long long b)
{
    return b ? gcd(b,a%b) : a;
}//gcd是为了约分
inline bool pd()
{
    for(int i=mxdep;i;i--)
        if(ans[i]!=w[i])
            return ans[i]&&ans[i]<w[i];
    return 0;
}//判断答案是否更优
inline void dfs(long long dep,long long a,long long b,long long mi)
{
    if(dep==mxdep)
    {
        if(b%a||(b<(mi-1)*a+1)||p.count(b/a)) return;//判断合法性
        //如果a/b可化为 1/k 的形式并且 1/k 可以选
        w[dep]=b/a;
        if(pd()) return;//判断答案是否更优
        memcpy(ans,w,sizeof(w)); flag=1;//更新
        return;
    }
    mi=max(mi,b/a+1);//求出下界
    for(long long i=mi;i;i++)
    {
        if( (mxdep-dep+1)*b<=i*a ) return;//上界
        if(p.count(i)) continue;//判断合法
        w[dep]=i;//记录路径
        long long xa=a*i-b,xb=i*b;//通分
        long long z=gcd(xa,xb);
        dfs(dep+1,xa/z,xb/z,i+1);//约分,向下一层
    }
}
int main()
{
    cin>>t;
    for(long long i=1;i<=t;i++)
    {
        flag=0; mxdep=1;
        cin>>A>>B>>n;
        long long c;
        p.clear();//细节
        while(n--) scanf("%lld",&c),p.insert(c);//存储不合法的数
        while(mxdep++)
        {
            memset(ans,0,sizeof(ans));
            dfs(1,A,B,B/A+1);
            if(flag) break;
        }//迭代加深
        printf("Case %lld: %lld/%lld=1/%lld",i,A,B,ans[1]);
        for(int i=2;i<=mxdep;i++)
            printf("+1/%lld",ans[i]);
        printf("\n");
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/LLTYYC/p/9612583.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值