【问题描述】
经由一些简单的规则而产生这一棵树,这棵树看起来大致这样:
你观察出规则了吗? (没有!!)
首先,他们在第一列放两个“分数”,第一个是 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组成的序列表示。
心路历程:在把第一题最小生成树打挂了并试图抢救未果后,开始看第二题的题面,发现可做,就开始对着这张奇怪的图开始试图模拟,然后又觉得可以记忆化搜索,没有成功,然后花了3小时才想出一个极其简单并刚好可以过的正解。
得分历程:3小时——100分 30分钟——骗分80
下面才是讲解:
思路:不难发现,一个结点的左儿子一定小于它,右儿子一定大于它,所以建一个名为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); //cout<<endl<<x<<' '<<y<<endl; 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); }