codevs 1288 埃及分数 迭代加深搜索

来源:互联网 发布:模拟退火算法题目应用 编辑:程序博客网 时间:2024/06/09 15:17

题目描述 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 longusing 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;}
原创粉丝点击