迭代加深,埃及分数

 

问题描述:

  给出一个分数,由分子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/3798

       同样,第二种情况更优。

解题思路:

(1)枚举深度maxd (从1开始)
(2)dfs搜索:从满足1/c<=a/b的最小c即b/a+1开始枚举分母,深度d(从0开始)为表示第几个分数;每次计算的是a/b - 1/i = a2/b2 然后将a2,b2再作为a,b进行递归,同时传入,下一个分母的可能最小值。 
(3)剪枝:if(bb * (maxd-d) <= i*aa) break;//剪枝:如果剩下的maxd+1-d个分数全部都是1/i,加起来仍然不超过aa/bb,则无解! (maxd-d)/i <= aa/bb

 

  1 //埃及分数
  2 
  3 #include<iostream>
  4 #include<algorithm>
  5 
  6 using namespace std;
  7 #define  M   20   //加数项的最大个数
  8 
  9 int MaxD;          //每次迭代加深时的深度
 10 bool flag ;        //当前深度下是否有解的标志
 11 int v[M], ans[M];  //v数组记录过程中的分母,ans记录最优分母
 12 
 13 int gcd(int a, int b)   //求最大公约数
 14 {
 15     int sum = 0;
 16     while (a)
 17     {
 18         sum = b%a;
 19         b = a;
 20         a = sum;
 21     }
 22     return b;
 23 }
 24 
 25 void better()   //求最优值
 26 {
 27     for (int i = MaxD-1; i >= 0; i--)  //i 表示下标
 28     {
 29         if (ans[i] == 0) //第一个满足条件的
 30         {
 31             for (int j = 0; j < MaxD; j++)
 32             {
 33                 ans[j] = v[j];
 34             }
 35             flag = true;
 36             break;
 37         }
 38         else if (v[i] > ans[i])
 39         {
 40             break;
 41         }
 42         else if (v[i] < ans[i])  //其他满足条件的情况
 43         {
 44             for (int j = 0; j < MaxD; j++)
 45             {
 46                 ans[j] = v[j];
 47             }
 48             flag = true;
 49             break;
 50         }
 51     }
 52 }
 53 
 54 bool dfs(int a, int b, int dep,int MC)
 55 {
 56     int c = 1;
 57     int aa = 1, bb = 1;
 58     int s = gcd(a, b);
 59     if (dep >= MaxD)
 60     {
 61         return flag;
 62     }
 63     else
 64     {
 65         a = a / s;
 66         b = b / s;
 67         if (a == 1)
 68         {//存在结果的出口,进行回溯
 69             v[dep] = b;
 70             better(); //搜索到解的时候,肯定是到达了MaxD层
 71             return flag;
 72         }
 73         else
 74         {
 75             c = max(b / a + 1, MC);
 76             for ( ;; c++)
 77             {
 78                 if (b*(MaxD - dep) <= c*a)  //当前深度下,后续分母最大值不能使等式成立
 79                 {
 80                     break;
 81                 }
 82                 v[dep] = c;
 83                 aa = a*c - b;
 84                 bb = b*c;
 85                 dfs(aa, bb, dep + 1,c+1);  //保证后一项的分母比前一项小
 86             }
 87             return flag;    
 88         }
 89     }
 90 }
 91 
 92 int main()
 93 {
 94     int a, b;
 95     while (cin >> a >> b)
 96     {
 97         memset(v, 0, sizeof(v));
 98         memset(ans, 0, sizeof(ans));
 99         flag = false;
100         //判断a,b合法性;
101         if (a > b || a*b == 0)
102         {
103             cout << "please enter the legal numbers!" << endl;
104             continue;
105         }
106         else
107         {
108             for (MaxD = 1;; MaxD++)
109                 if (dfs(a,b,0,1))  //存在结果则停止迭代搜索,跳出循环
110                     break;
111         }
112         //输出不等式
113         cout << a << "/" << b << "=";
114         for (int i = 0; i < MaxD-1; i++)  //退出循环,MaxD没有改变
115         {
116             cout << 1 << "/" << ans[i] << "+";
117         }
118         cout << 1 << "/" << ans[MaxD - 1] << endl << endl;  //最后一个项
119     }
120 }
View Code

 

转载于:https://www.cnblogs.com/lixiaomo/p/7810998.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值