題目:埃及分數ide
題目連接:http://codevs.cn/problem/1288/spa
題目大意:code
給出一個分數,由分子a 和分母b 構成,如今要你分解成一系列互不相同的單位分數(形如:1/a,即分子爲1),要求:分解成的單位分數數量越少越好,若是數量同樣,最小的那個單位分數越大越好。blog
如:string
19/45 = 1/3 + 1/12 + 1/180;it
19/45 = 1/5 + 1/6 + 1/18;io
以上兩種分解方法都要3個單位分數,但下面一個的最小單位分數1/18比上一個1/180大,因此第二個更優。event
思路:class
雖然是求最優解,但這道明顯不是廣搜吧(空間要求過高),並且很明顯是用深搜作,即從1~無窮,每個分母,都有選中和不選中兩種狀態,若是選中,那麼就減去這個分數,沒有就是跳過,但無窮究竟是哪裏呢,並且具體要選幾個呢,這就是這道題的難點。由於多組答案的優先級是由單位分數的個數首先決定,那麼咱們能夠逐次放寬個數的限制,即迭代加深搜索。cli
迭代加深搜索的具體內容:第一次,我限制只能用k個單位分數來完成,若是找完全部狀況,仍是沒找到解,那麼我如今用k+1個單位分數解決,這樣優勢以下:
1. 確定保證最優解,由於我用k個來完成分解就說明比k小的個數分解不出來,若是分解得出來,我在那時就退出循環了。
2. 雖然看似重複進行了不少遍的搜索,但上一層的搜索量和下一層比起來太少了,不影響總的時間複雜度。
拋開逐步解開個數限制外,每個個數限制下的作法和日常的深刻優先搜索大體相同,要注意剪枝!
主要的兩個剪枝以下:
1. 限制開頭:並非每次都要從1開始遍歷分母,假設如今要分解a/b,那麼分母b/a就是起點,由於b/a的分數太大,起始點已經超過了a/b,沒有什麼意義:1/(b/a)=a/b ,假設起點s<b/a,那麼顯而易見,起點的分數已經比咱們要的總和(a/b)大了。
2. 限制結尾:
(1)比較簡單的限制結尾能夠這樣看:若是我已經找到分母k了,而如今要分解得分數是a/b,如今還要找m個單位分數,那麼能夠想象:有可能 m * ( 1/k ) 還小於a/b,也就是說就算全是1/k,我湊夠m個,也達不到a/b,那麼說明前面的分配方案確定有無,直接能夠return了。加上這個剪枝已經能夠獲得答案了,只是時間有點慢罷了。
(2)如今咱們假設終點爲t,還要找m個單位分數,如今的分數剩下a/b,那麼很容易有m * (1/t) <= a / b ,也就是說我若是m個都用最小的,確定小於等於a/b。(等於號就是說有可能m=1,我可能直接終點就是答案,若是m>1,那麼終點確定也不可能選到,假設選了(後面還要選(m-1)個,確定湊不夠)這樣子,由上面的式子,通過變換,能夠獲得 t >= m*b/a ,也就是說終點爲m*b/a。
有了思路代碼仍是很容易的:
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 5 int gcd(int a,int b) 6 { 7 return b?gcd(b,a%b):a; 8 } 9 10 int ans[1000],ao; 11 int out[1000],oo; 12 13 void dfs(int limit,int h,int ma,int mb) 14 { 15 if(h==limit) return ; 16 if(mb%ma==0 && mb/ma>ans[ao-1] && ( oo<=0 || mb/ma < out[oo-1] )) 17 { 18 ans[ao++]=mb/ma; 19 oo=ao; 20 memcpy(out,ans,sizeof(ans)); 21 ao--; 22 return ; 23 } 24 int i=mb/ma-1; 25 if(i<=ans[ao-1]) i=ans[ao-1]; //ans[ao-1]就是前面找過的最後一個,這前面的都處理過(選中or不選中) 26 int j=(limit-h)*mb/ma; 27 while(++i<=j) 28 { 29 if(oo>0&&i>=out[oo-1]) return ; 30 int g=gcd(i,mb); 31 int k=i/g; 32 //if(ma*i/mb+h>limit) return ; 33 int x=mb*k; 34 int y=ma*k-mb/g; 35 if(y<0) continue; 36 ans[ao++]=i; 37 if(y==0) 38 { 39 oo=ao; 40 memcpy(out,ans,sizeof(ans)); 41 ao--; 42 return ; 43 } 44 dfs(limit,h+1,y,x); 45 ao--; 46 } 47 } 48 49 int main() 50 { 51 int a,b; 52 while(scanf("%d%d",&a,&b)!=EOF) 53 { 54 ao=0; 55 oo=0; 56 for(int i=1;i<100;i++) 57 { 58 //printf("%d\n",i); 59 dfs(i,0,a,b); 60 if(oo>0) break; 61 } 62 for(int i=0;i<oo;i++) 63 { 64 if(i!=0) printf(" "); 65 printf("%d",out[i]); 66 } 67 printf("\n"); 68 } 69 return 0; 70 }