埃及分数 (迭代深搜 TAT....入门)

描述

在古埃及,人们使用单位分数的和(形如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<a<b<1000),编程计算最好的表达方式。
输入
输入:a b
输出
若干个数,自小到大排列,依次是单位分数的分母。
如果存在多个解,则输出任意一个。

样例输入
19 45
样例输出
5 6 18

思路

最主要的就是如何枚举分母?如何确定分母的枚举范围?
1.下界的选择:
a.由题意可知,我们解是从小到大输出,即(1/x + 1/y + ... + 1/z ,x  x*i>y)。

因此,下界的选择是满足上述2种情况下,取2者中的最大值。

2.上界的选择:
若枚举的总个数为c个,当前已经枚举了a个,则我们还需要枚举b=c-a 个。当前枚举的分数1/i。
因为我们的分数枚举是越来越小的。
那么我们就要保证 ( b/i > x/y ) ,即假设最优情况下的b个分数全是1/i,那么b个分数和必定大于 x/y。
若( b/i < x/y ) 那么剩下b个数累加起来是一定不满足条件的。

因此我们上界满足的条件 :( b/i > x/y ) ===> b*y> x*i

注意:分母扩大的过程中可能会爆int,因此省力直接开LL防止爆数据

参考代码

#include 
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define LL long long
#define PII pair
#define PIII pair
#define PSI pair
#define PIIS pair >
#define PDD pair
using namespace std;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
const int M=1e7;
const int mod=1e9+7;
/*
    思路:
    如何枚举分母?如何确定分母的枚举范围?

    1.下界的选择:
    a.由题意可知,我们解是从小到大输出,即(1/x + 1/y + ... + 1/z ,x  x*i>y)。

    因此,下界的选择是满足上述2种情况下,取2者中的最大值。

    2.上界的选择:
    若枚举的总个数为c个,当前已经枚举了a个,则我们还需要枚举b=c-a 个。当前枚举的分数1/i。
    因为我们的分数枚举是越来越小的。
    那么我们就要保证 ( b/i > x/y ) ,即假设最优情况下的b个分数全是1/i,那么b个分数和必定大于 x/y。
    若( b/i < x/y ) 那么剩下b个数累加起来是一定不满足条件的。

    因此我们上界满足的条件 :( b/i > x/y ) ===> b*y> x*i

    注意:分母扩大的过程中可能会爆int,因此省力直接开LL防止爆数据
*/
LL path[1111]; //某次搜索解
LL ans[1111]; //最优解
int depth;
LL gcd(LL q,LL w)
{
    return w==0?q:gcd(w,q%w);
}
//求小于x/y最大的1/i的分母i
LL least(LL x,LL y)
{
    // for(int i=2;;i++)
    // {
    //     if(x*i>y)return i;
    // }
    return y/x+1;
}
// 当前剩余x/y ,当前层数cur ,当前可选的最小分母
bool dfs(LL x,LL y,int cur,LL prem)
{
     
    //到达枚举层数,显然只能取x/y,对x/y进行判断
    if(cur==depth)
    {
        //无法整除,必定不能化简为 1/i 的形式
        if(y%x!=0)return false;  //或者是 x==1的时候

        //找到可行解
        path[cur]=y/x;
        // for(int i = 0;i <=cur;i++)
        // {
        //         cout< x*i 上界
    for(LL i=max(prem,least(x,y));(depth-cur+1)*y > x*i ;i++)
    {
        //记录路径
        path[cur]=i;
        //剩余分子
        LL xx=x*i-y;
        //剩余分母
        LL yy=y*i;
        LL d=gcd(xx,yy);

        
        // if(dfs(xx/d,yy/d,cur+1,i+1))return true;
        //此处应标记,不应直接返回,不然只搜索到第一个解就返回了。
        if(dfs(xx/d,yy/d,cur+1,i+1))flag=true;
    }
    return flag;
}
int main()
{
    // io;
    int a,b;
    cin>>a>>b;
    int d=gcd(a,b);
    //先化简式子
    a/=d;
    b/=d;
    //特判情况
    if(a==1)
    {
        cout<

你可能感兴趣的:(算法,c++)