思路:第一眼看到这个题目"按层遍历",联想起BFS,用一个队列来模拟,这样子就需要建立一棵二叉树,明显的空间复杂度不符合要求,但是不用结点指针的类型来模拟二叉树,还有什么办法可以做到呢?又联想到堆,堆的特点是A[1]是根,如果当前根是A[i],则左结点是A[i*2],右结点是A[i*2+1],知道这个特性,又如何呢?先看题目:
任意一个节点p/q的左儿子节点和右儿子节点分别是,p/(p+q)和(p+q)/q。
翻译上面这句话就是,
如果A[i]的键值是p/q,则A[left]=p/(p+q),即分子不变,分母q=p+q;(这是一个很重要的迭代式)
同样,A[right]=(p+q)/q,分母不变,分子p=p+q;(迭代式)
而我们已经知道当前根的键值A[1],p=1,q=1;
现在问题转化成,如果我们知道某一个位置n,要求A[n]的键值p/q,要怎么做?
答案是:我们只需要知道从1到n的路径,然后用上面的迭代式一直从根往下迭代即可.所以,首要任务,就是找到从根到这个结点的路径!
要找到从根到这个结点的路径其实也很容易,就是这个结点每次都除以2再向下取整即可得到父节点.路径可以用一个vector保存起来.知道路径之后,就可以根据上面思路写出代码.
题目还有一个要求,就是知道当前的键值p/q,如何找到这是第几个结点?
有了上面的思路,我们容易联想到,找到路径问题就会迎刃而解,观察可以知道,如果p>q,则这是右结点,否则这是左结点,根据这个也可以继续向上迭代了.
第一次代码:
#include<iostream> #include<vector> using namespace std; typedef unsigned long long int64; struct node { int64 p; int64 q; }; //vector<node> A; vector<int64 > vec; vector<node > vec2; node no,y,x; void find( node n) { vec2.push_back(n); no.q=no.p=-1; while(1) { if( n.q ==1 && n.p ==1) break; if( n.q > n.p) { no.q=n.q-n.p; no.p=n.p; vec2.push_back(no); } else { no.q=n.q; no.p=n.p-n.q; vec2.push_back(no); } n=no; } int64 sum=1; for(int i=vec2.size()-2;i>=0;--i) { if( vec2[i].p > vec2[i].q ) { sum = sum * 2+1; } else { sum = sum * 2; } } cout<<sum<<endl; } void set(int64 n) { for(int64 i=n;i>=1;i /=2) vec.push_back(i); //A[1].q=A[1].p=1; y.q=y.p=x.p=x.q=1; for(int i=vec.size()-2 ; i>=0 ; --i ) { if( (vec[i] & 1) ==1 )//奇数,右子树 { x.p = y.p + y.q ; //A[vec[i+1]].p=A[vec[i]].p+A[vec[i]].q; x.q = y.q ; //A[vec[i+1]].q=A[vec[i]].q; } else//左子树 { x.p = y.p; //A[vec[i+1]].p=A[vec[i]].p; x.q = y.q + y.p ; //A[vec[i+1]].q = A[vec[i]].q+A[vec[i]].p; } y=x; } cout<<x.p<<" "<<x.q<<endl; } int main() { int64 T,a,b,c; node n; cin>>T; while(T--) { cin>>a; if( a==1) { cin>>b; vec.clear(); set(b); } else if(a==2) { //cout<<A[5].p<<A[5].q<<endl; vec2.clear(); cin>>b>>c; if( b==c) continue; n.q=c; n.p=b; find(n); } } }上面的代码虽然可以AC,但是,看起来比较混乱,后来发现,将每个结点存起来似乎并不优化,因为这是二叉树,我们只需要存当前结点是左结点还是右结点即可,用一位就可以表示,空间上得到不少优化,而且看起来比较清晰.
优化后:
#include<iostream> #include<vector> using namespace std; typedef unsigned long long int64; vector<int64> path; void FindTheNode(int64 x) { int64 y; path.clear(); while(x!=1) { y=x >> 1; if( (y<<1) == x) //left node path.push_back(1); //1 means left else path.push_back(0); //0 means right x=y; } int64 p=1,q=1; for(int i=path.size()-1;i>=0;--i) { if( path[i] == 1) q=p+q; else p=p+q; } cout<<p<<' '<<q<<endl; } void FindTheNum(int64 p,int64 q) { path.clear(); while(p != q) //当不到根时,继续循环 { if( p > q) { path.push_back(0); p = p-q; } else { path.push_back(1); q = q-p; } } int64 x=1; for(int i=path.size()-1;i>=0;--i) { if( path[i] == 1) x= (x<<1); else x=(x<<1) + 1; } cout<<x<<endl; } int main() { int64 T,p,q,n,Num; cin>>T; while(T--) { cin>>n; if( n == 1) { cin >> Num; FindTheNode(Num); } else if( n == 2) { cin>>p>>q; FindTheNum(p,q); } } return 0; }温馨提示:上面说的取值是1-2^64需要用unsigned long long型!!!,我就是在这里wa了好多次.