埃及分数问题 迭代加深搜索(IDDFS)

题目传送门:【传送门1】


题目大意:在古埃及,人们使用单位分数的和(形如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 的最好的表达方式。


题目分析:

由题,显然这是一道搜索题= =

分析题目,直接用 DFS 容易导致无限递归,直接用 BFS 不容易搜出结果。由于题目要求求出加数最少,最小分母最大的方式,所以我们可以考虑使用迭代加深搜索 IDDFS。

整体思路:首先,按照加数个数,从少到多慢慢增加;当一个特定的加数个数搜到了结果,我们就只在这一层进行搜索(也就是不再往下搜,即不再增加加数的个数)。对于分母,每次我们从小到大进行循环。当我们在这一层已经无法找到解的时候,及时退出(详见代码)。最后输出答案即可。

下面附上代码:

[cpp] view plain copy
print ?
  1. #include  
  2. #include  
  3. #include  
  4. using namespace std;  
  5. typedef long long LL;  
  6. const int MX=1005;  
  7.   
  8. int maxd=1,mind=1;  
  9. LL a,b,den[MX*10],ans[MX*10];  
  10. //den:denominator,临时保存单位分数的分母   
  11. //ans:answer,保存最终答案代表的分数的分母  
  12. //因为都是单位分数,分子为 1,所以只需保存分母即可   
  13.   
  14. LL gcd(LL a,LL b){return b==0 ? a : gcd(b,a%b);}  
  15.   
  16. int starT(LL x,LL y){                   //确定分母的下界   
  17.     for (int i=2;;i++){                 //即:1/i <= x/y   
  18.         if (y<=x*i){  
  19.             return i;  
  20.         }  
  21.     }  
  22. }  
  23. bool can_update(int d){             //判断是否更新,如果最小分数的分母比原来的大,那么更新   
  24.     if (ans[d]==-1) return true;   
  25.     if (den[d]>ans[d]) return false;  
  26.     if (den[d]return true;  
  27.       
  28.     for (int i=1;i<=d;i++){          //否则从第一个分数开始判断(不同的题目有不同的要求)   
  29.         if (ans[i]==-1) return true;  
  30.         if (den[i]>ans[i]) return false;  
  31.         if (den[i]return true;  
  32.     }  
  33.     return false;  
  34. }  
  35. bool iddfs(int d,int min_den,LL x,LL y){//min_den:最小的分母值   
  36.     if (d==maxd){  
  37.         if (y%x!=0) return false;       //不是单位分数   
  38.         den[d]=y/x;                     //否则就是一个合法答案,判断是否更新   
  39.         if (can_update(d)){  
  40.             for (int i=1;i<=d;i++){  
  41.                 ans[i]=den[i];  
  42.             }  
  43.         }  
  44.         return true;  
  45.     }  
  46.     min_den=max(min_den,starT(x,y));  
  47.     bool ok=false;  
  48.     for (int i=min_den;;i++){  
  49.         if (y*(maxd-d+1)<=i*x)  
  50.             break;      //如果之后选的分数大小不超过x/y,则当前加数已经不满足,返回   
  51.                         //即:(maxd-d+1)*(1/i) <= x/y 时,返回  
  52.         den[d]=i;  
  53.         LL xx=x*i-y,yy=y*i,g=gcd(xx,yy);  
  54.         if (iddfs(d+1,i+1,xx/g,yy/g))  
  55.             ok=true;  
  56.     }  
  57.     return ok;  
  58. }  
  59.   
  60. int main(){  
  61.     scanf(”%lld%lld”,&a,&b);  
  62.     if (b%a==0){                //如果一开始就是单位分数了,那么直接返回   
  63.         printf(”%lld ”,b/a);  
  64.         return 0;  
  65.     }  
  66.     mind=starT(a,b);  
  67.     while (++maxd){  
  68.         memset(ans,-1,sizeof(ans));  
  69.         if (iddfs(1,mind,a,b)){  
  70.             break;  
  71.         }  
  72.     }  
  73.     for (int i=1;i<=maxd;i++){  
  74.         printf(”%lld ”,ans[i]);  
  75.     }  
  76.     return 0;  
  77. }  

你可能感兴趣的:(各大OJ专题(POJ,BZOJ,hdu等),模板题,迭代加深(IDDFS))