分數樹
19世紀的時候,Moriz Stern(1858)與Achille Brocot(1860)發明了“一棵樹”。據說,經由一些簡單的規則而產生的這一棵樹上,可以包含零以上所有的有理數。這棵樹看起來大致這樣: 此題 列 對應 我們傳統的行
你觀察出規則了嗎?
首先,它們在第一列放兩個“分數”,第一個是0/1,代表0;第二個是1/0,代表無窮大。接著它們一列一列的產生這棵樹,當它們要產生第k+1列的時候,就先把前k列所有的分數按照大小排成一列(假設有n個),在這些數之間會有n-1個間隔,那麼第k+1列就準備產生n-1個數,其值的分子恰好是左右兩個數的分子的和,分母是左右兩個數的分母的和。
例如,2/3,而它的2就是左邊1/2的1和右邊1/1的分子1相加的結果;而2/3的3,則是1/2的2加上1/1的分母1而得。 從這棵樹中,我們可以看出,每個正的最簡分數在這棵樹中恰好出現一次,我們用字母“L”和“R”分別表示從樹根(1/1)開始的一步“往左走”和“往右走”,則每一個數都可以由L和R組成的序列表示。
例如,LRRL表示從1/1開始往左走一步到1/2,然後往右走到2/3,再往右走到3/4,最後往左走到5/7。我們可以把LRRL看作5/7的一種表示法。幾乎每個正分數均有唯一的方法表示成一個由L和R組成的序列。
給定一個分數,輸出它的LR表示法。
【輸入】
輸入檔案fraction.in有兩個互為素數的正整數m和n(1<=n,m<=1000)。
【輸出】
輸出檔案fraction.out為對應的LR表示法。
【輸入】
5 7
【輸出】
LRRL
思路(參考了網上dalao的基本思路)
一個結點的左兒子一定小於它,右兒子一定大於它,所以建一個名為cc[ ]的結構體,儲存它左邊那位的分子,分母以及右邊那位的分子,分母以及自己的分子,分母;
將根結點初始化為1/1,並將它的左邊右邊以及它自己錄入,然後開始搜尋;
並且你又會發現,如果要尋找的那個比它小時,就會往左,接下來的那個結點就會繼承它的左邊,而那個結點的右邊就會更新為當前搜尋的結點,然後再繼續搜尋那個結點,一直到當前搜尋的結點的值與要尋找的分數相等時就退出搜尋。
程式碼
#include<bits/stdc++.h> using namespace std; double a,b,x,y; struct fs{ double left1,left2,right1,right2; double fm,fz; }cc[1000050]; int find(int n){ y=(cc[n].fz*1.0)/(cc[n].fm*1.0); if(x<y){ cc[n+1].left1=cc[n].left1; cc[n+1].left2=cc[n].left2; cc[n+1].right1=cc[n].fz; cc[n+1].right2=cc[n].fm; cc[n+1].fz=cc[n].left1+cc[n].fz; cc[n+1].fm=cc[n].left2+cc[n].fm; cout<<"L"; return find(n+1); } if(x>y){ cc[n+1].left1=cc[n].fz; cc[n+1].left2=cc[n].fm; cc[n+1].right1=cc[n].right1; cc[n+1].right2=cc[n].right2; cc[n+1].fz=cc[n+1].left1+cc[n+1].right1; cc[n+1].fm=cc[n+1].left2+cc[n+1].right2; cout<<"R"; return find(n+1); } return 0; } int main(){ freopen("fraction.in","r",stdin); freopen("fraction.out","w",stdout); cin>>a>>b; if(a==b)return 0; cc[0].left1=0; cc[0].left2=1; cc[0].right1=1; cc[0].right2=0; cc[0].fm=1; cc[0].fz=1; x=(a*1.0)/(b*1.0); find(0); }