埃及分数


description


对于每一个非负有理数,我们知道它一定能划归成某些特殊真分数之和,特殊真分数要满足它们的分子为1,但是我们知道,对于无穷级数1/2+1/3+1/4…。虽然,它是发散的,但是改级数增长得极为缓慢,例如到了数百万之后,和也在18~19左右。

若干年来,不断有人宣称发现了该级数的特殊性质,这些都对这个问题的研究起到了深远的影响。

你的任务来了,要求给你个真分数,你需要将其化简为最少的若干特殊真分数之和,你要输出这个序列(序列按递增序)。

如果有不同的方案,则分数个数相同的情况下使最大的分母最小。若还相同,则使次大的分母最大……以此类推。

如: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

最好的是最后一种,因为18 比180, 45, 30,都小。

对于此类搜索问题,搜索深度没有明显的上界,而且加数的选择在理论上也是无限的,也就是说宽度搜索连一层都扩展不完。

利用迭代加深搜索(IDA*)可以解决上面的问题。一方面我们要解决,利用DFS从小到大枚举深度上限maxd,虽然理论上深度是无限的,但是只要保证有解,深度值必然在有限的时间内能枚举到。

另一方面,我们还要解决每次枚举的值无限的问题,可以借助maxd来“剪枝”,以此题为例,每一次枚举的的分母个数必然有一个结束值,否则就会出现无限下去的尴尬了。那么当扩展到第i层时,第i层分数值为1/e,那么接下来由于分母是以递增顺序进行的,那么分数值都不会大于1/e,如果c/d(前i个分数之和)+1/e*(maxd-i)<a/b,可以直接break;因为起码深度不够,要再加深度。

基本框架:

for(maxd=1;;maxd++)
{
      if(dfs(0,,,))
     {
         ok=1;
         break;
      }
}

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 20000
//一定要用64位的整数,由于里面存在化归为同分母的操作,需要相乘
using namespace std;
int64_t maxd;
int64_t v[MAXN],ans[MAXN];//v是暂时存放的满足题意的分母的数组,ans是记录满足题意的最优分母值的数组。
int64_t gcd(int64_t a,int64_t b)//求最大公约数
{
    int64_t m;
    while(a!=0)
    {
       m=b%a;
       b=a;
       a=m;
    }
    return b;
}
int64_t getfirst(int64_t a,int64_t b)//取比a/b小的最大分数,分子必须为1
{
    int64_t i,j;
    for(i=2;;i++)
    {
        if(b<a*i)
        {
                break;
        }
    }
    return i;
}
bool better(int64_t d)//必要的判断,这组分数之和是否满足最优解
{
    int64_t i;
    bool flag=true;
    if(ans[d]==-1)//由于初始化ans是-1,如果是第一次出现的满足题意的解,返回true
        return true;
    for(i=d;i>=0;i--)
    {
        if(v[i]==ans[i])//从高位进行判断大小,题意要求
            continue;
        else if(v[i]>ans[i])
        {
               //return false;
               flag=false;
               break;
        }
        else
        {
                break;
        }
    }
    if(flag==true)
        return true;
    else
        return false;
}
bool dfs(int64_t a,int64_t b,int64_t from,int64_t d)//深度为d
{
    int64_t aa,bb,g,i;
    bool ok;
   // cout<<from<<endl;
    if(d==maxd)
    {
        if(a!=1)
            return false;
        for(i=0;i<=d-1;i++)
        {
            if(v[i]==b)
            {
                return false;
            }

        }
        v[d]=b;
        sort(v,v+d);
        if(better(d))
            memcpy(ans,v,sizeof(int64_t)*(d+1));
        return true;
    }
    ok=false;
    //重要!!!
    from=max(from,getfirst(a,b));//枚举起点,去上一次加一的分母值和比a/b小的最大分数的分母中更大的。
    for(i=from;;i++)
    {
        //剪枝,如果c/d(前i个分数之和)+1/e*(maxd-i)<a/b,可以直接break;
        if(b*(maxd+1-d)<=a*i)
            break;
        v[d]=i;
        aa=a*i-b;
        bb=b*i;
        g=gcd(aa,bb);
        aa=aa/g;
        bb=bb/g;//约分
        if(dfs(aa,bb,i+1,d+1))
            ok=true;
    }
    return ok;
}
int main()
{
    int64_t a,b,aa,bb,i,g;
    while(cin>>a>>b)
    {
        if(a==0)
        {
            cout<<a<<"/"<<b<<"=0"<<endl;
            continue;
        }
        memset(ans,-1,sizeof(ans));
        g=gcd(a,b);
        aa=a/g;
        bb=b/g;
        if(aa==1)
        {
            printf("%d/%d=%d/%d\n",a,b,aa,bb);
        }
        else
        {
            for(maxd=1;;maxd++)
            {
                if(dfs(aa,bb,getfirst(aa,bb),0))
                    break;
            }
            cout<<a<<"/"<<b<<"=";
            for(i=0;i<=maxd-1;i++)
                cout<<"1/"<<ans[i]<<"+";
            cout<<"1/"<<ans[i];
            cout<<endl;
        }
    }
    return 0;
}

方法:迭代加深解答树并进行剪枝
剪枝点:

    1. 要表示的分数与已经分配到分数总和的差值要大于0才继续尝试
    2. 根据剩余深度和上面的差值确定每层尝试分数分母的上限,下限即前一分配好的分数的分母加1

      #include <iostream>
      #include <math.h>
      using namespace std;
      #define N 10000000
      int array[N];  
      
      double is_equal(int a, int b, int cur)
      {
          double sum = 0;
          for (int i = 0; i <= cur; i++)
              sum += (double)1 / array[i];
          return (double)a / b - sum;
      }  
      
      int dfs(int cur, int d, int a, int b)
      {
          if (cur == d)
              return 0;
          int start = (int)(d - cur) / (is_equal(a, b, cur - 1));
          int end = cur == 0 ? 2 : array[cur-1] + 1;
          for (int i = start;i>=end; i--)
          {
              array[cur] = i;
              double p = is_equal(a, b, cur);
              if (abs(p) <= 0.00001)
              {
                  for (int j = 0; j <= cur; j++)
                      cout << array[j] << ‘ ‘;
                  cout << endl;
                  return 1;
              }
              else if (p > 0)
              {
                  if (dfs(cur + 1, d, a, b))
                      return 1;
              }
          }
          return 0;
      }  
      
      int main()
      {
          int a, b;
          cin >> a >> b;
          for (int i = 1; ; i++)
          {
              //cout << i << endl;
              if (dfs(0, i, a, b))
                  break;
          }
      }  
时间: 2024-09-24 08:29:29

埃及分数的相关文章

1288 埃及分数

1288 埃及分数 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 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/

【迭代加深搜索】埃及分数问题

谢谢阿苏~http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5856756 [迭代加深搜索(ID,iterative deepening)]:从小到大枚举上限maxd,每次执行只考虑深度不超过maxd的结点. ------对于可以用回溯法求解但解答树的深度没有明显上限的题目,可以考虑ID算法: ------优点:它主要是在递归搜索函数的开头判断当前搜索的深度是否大于预定义的最大搜索深度,如果大于,就退出这一层的搜索,如果不大于,就

将真数分解为埃及分数

问题: 输入一个分数,将该分数分解为埃及分数. 真分数:分子小于分母的分数 埃及分数:分子为一的分数 #include <stdio.h> #include <stdlib.h> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ int main(int argc, char *argv[]) { long in

埃及分数问题_迭代加深搜索_C++

一.题目背景 http://codevs.cn/problem/1288/ 给出一个真分数,求用最少的1/a形式的分数表示出这个真分数,在数量相同的情况下保证最小的分数最大,且每个分数不同. 如 19/45=1/3 + 1/12 + 1/180 二.迭代加深搜索 迭代加深搜索可以看做带深度限制的DFS. 首先设置一个搜索深度,然后进行DFS,当目前深度达到限制深度后验证当前方案的合理性,更新答案. 不断调整搜索深度,直到找到最优解. 三.埃及分数具体实现 我们用dep限制搜索层数,先从2开始,每

埃及分数,迭代加深

埃及分数题意非常明白,这里就不解释了…… 但是关于最优解的问题,题目并没有说明 原题: 对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好. 然而这并没有考虑一种很恶心的情况:加数一样多,而且最后一个分数一样的时候,哪种情况最优呢? 我先写了个从末尾到开头一位一位判断大小,保证最大的程序,大概只有一半的分数(codevs) 又写了个输出 最先找到的 末尾最大的 方案 的程序,结果90(还是codevs) 我选择放弃,,,

将真分数分解为埃及分数

描述 分子为1的分数称为埃及分数.现输入一个真分数(分子比分母小的分数,叫做真分数),请将该分数分解为埃及分数.如:8/11 = 1/2+1/5+1/55+1/110. 接口说明 /* 功能: 将分数分解为埃及分数序列 输入参数:     String pcRealFraction:真分数(格式“8/11”) 返回值:     String pcEgpytFraction:分解后的埃及分数序列(格式“1/2+1/5+1/55+1/100”) */ public static String  Co

“埃及分数”问题浅谈对迭代加深搜索的理解

迭代加深搜索(IDDFS)的思想 迭代加深搜索一般用来求解状态树"非常深",甚至深度可能趋于无穷,但是"目标状态浅"的问题.如果用普通的DFS去求解,往往效率不够高.此时我们可以对DFS进行一些改进.最直观的一种办法是增加一个搜索的最大深度限制maxd,一般是从1开始.每次搜索都要在maxd深度之内进行,如果没有找到解,就继续增大maxd,直到成功找到解,然后break. 如下图所示,如果用DFS,需要15步才能找到结点3,但是用迭代加深搜索,很快即可找到结点3.

迭代加深,埃及分数

问题描述: 给出一个分数,由分子a 和分母b 构成,现在要你分解成一系列互不相同的单位分数(形如:1/a,即分子为1),要求:分解成的单位分数数量越少越好,如果数量一样,最小的那个单位分数越大越好. 如: 19/45 = 1/3 + 1/12 + 1/180; 19/45 = 1/5 + 1/6 + 1/18; 由于,1/18比1/180更大,所以第二种情况更优. 59/211=1/4 + 1/36 + 1/633 + 1/3798 59/211=1/6 + 1/9 + 1/633 + 1/37

Vijos 1308 埃及分数(迭代加深搜索)

题意: 输入a.b, 求a/b 可以由多少个埃及分数组成. 埃及分数是形如1/a , a是自然数的分数. 如2/3 = 1/2 + 1/6, 但埃及分数中不允许有相同的 ,如不可以2/3 = 1/3 + 1/3. 求出可以表达a/b个数最少埃及分数方案, 如果个数相同则选取最小的分数最大. #include <bits/stdc++.h> #define LL long long using namespace std; int maxd; long long v[1234],ans[1234

埃及分数&amp;&amp;The Rotation Game&amp;&amp;骑士精神——IDA*

IDA*:非常好用的搜索,可以解决很多深度浅,但是规模大的搜索问题. 估价函数设计思路:观察一步最多能向答案靠近多少. 埃及分数 题目大意: 给出一个分数,由分子a 和分母b 构成,现在要你分解成一系列互不相同的单位分数(形如:1/a,即分子为1),要求:分解成的单位分数数量越少越好,如果数量一样,最小的那个单位分数越大越好. 如: 19/45 = 1/3 + 1/12 + 1/180; 19/45 = 1/5 + 1/6 + 1/18; 以上两种分解方法都要3个单位分数,但下面一个的最小单位分