codevs 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

输入描述 Input Description

a b

输出描述 Output Description

若干个数,自小到大排列,依次是单位分数的分母。

样例输入 Sample Input

19 45

样例输出 Sample Output

5 6 18

题解:
好的我知道我已经废掉了。
枚举加数,判断是否有解,由于加数个数的上限不好确定,所以是迭代加深搜索。
搜索的时候定义状态为当前深度,允许深度,前一个分母(由于是递增的顺序枚举分母),还需要凑成的分数值(写成分子分母的形式)。
用实数的话。。。很麻烦,掉精度什么的
如果只剩下一个分数的位置的话,就直接判断是否是单位分数,然后判断是否可以更新就可以了。
重点在枚举分母上,因为分母的范围其实很大。由于定义了是从小到大枚举分母,所以当前分母的最小值应该比前一个大(这也是为什么要记录下前一个分母的值),这就确定了分母的下界。至于上界,然后由于分母单增,所以分数值单减。设我们还剩下x个加数需要填,则每个分数均摊下来都应该是a/(bx),而当前分数是最大的,所以它一定不能小于这个值,即分母不能大于(bx/a)。在规定的范围内枚举更新就可以了。
woc我有罪我之前的代码有毛病,现已更正。。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;

const int N = 1000 + 10;

inline ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b);}

void modify(ll &a,ll &b){
    ll k=gcd(a,b);
    a/=k,b/=k;
}

ll a,b;
ll c[N],tmp[N];

bool check(int n){
    if(c[n]==-1) return true;
    if(c[n]>tmp[n]) return true;
    return false;
}

bool dfs(int d,int pre,int limit,ll a,ll b){
    if(d>limit) return false;
    modify(a,b);
    if(d==limit){
        if(a==1&&b>pre){
            tmp[limit]=b;
            if(check(limit)){
                for(int i=1;i<=limit;++i) c[i]=tmp[i];
                return true;
            }
        }
        return false;
    }
    bool tc=0;
    for(int i=pre+1;i<=ceil(1.0*b*(limit-d+1)/a);++i){
        tmp[d]=i;
        tc|=dfs(d+1,i,limit,i*a-b,i*b);
        tmp[d]=0;
    }
    return tc;
}

int main(){
    cin>>a>>b;
    int ans=1;
    modify(a,b);
    memset(c,-1,sizeof(c)); 
    for(;;++ans){
        if(dfs(1,1,ans,a,b)) {
            for(int i=1;i<=ans;++i) printf("%d ",c[i]);
            break;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值