UVa679-Dropping Balls-小球下落-二叉樹的編號

題目鏈接:https://vjudge.net/problem/UVA-679 本題程序並不是按照OJ輸入輸出的!

有一棵二叉樹,最大深度爲D,且所有葉子的深度都相同。所有結點從上到下從左到右編號爲1, 2, 3,…, 2D-1。在結點1處放一個小球,它會往下落。每個內結點上都有一個開關,初始全部關閉,當每次有小球落到一個開關上時,狀態都會改變。當小球到達一個內結點時,如果該結點上的開關關閉,則往左走,否則往右走,直到走到葉子結點,如下圖所示。


圖  所有葉子深度相同的二叉樹


一些小球從結點1處依次開始下落,最後一個小球將會落到哪裏呢?輸入葉子深度D和小球個數I,輸出第I個小球最後所在的葉子編號。假設I不超過整棵樹的葉子個數。D≤20。輸入最多包含1000組數據。 

樣例輸入:
4 2
3 4
10 1
2 2
8 128
16 12345
樣例輸出:

12
7
512
3
255
36358


分析:

1、完全二叉樹中,對於一個結點k,其左子結點、右子結點的編號分別是2k和2k+1。

2、開闢數組直接模擬狀態。下面代碼中,1<<n 代表1往左移n位,二進制表示。所以1<<20就是2^20

方法一:

#include<iostream>
#include<cstring>
using namespace std;
const int maxd=20;
int s[1<<maxd];
int main(){
    int D,I;
    while(cin>>D>>I){
        memset(s,0,sizeof(s));
        int k=1;
        while(I--){
            k=1;
            for(int i=1;i<=D;i++){
                if(s[k]==0) {
                    s[k]=1;
                    k=2*k;
                }else{
                    s[k]=0;
                    k=2*k+1;
                }
            }
        }
        cout<<k/2<<endl;
    }
    return 0;
}


/*
for循環也可以這樣寫
for(int i=1;i<=D;i++){
    s[k]=!s[k];
    k=s[k]?2*k:2*k+1;
}
*/

方法改進:

此程序運算量太大。由於I可以高達2D-1,每組測試數據下落總層數可能會高達219*19=9961472,而一共可能有10000組數據…… 

每個小球都會落在根結點上,因此前兩個小球必然是一個在左子樹,一個在右子樹。一般地,只需看小球編號的奇偶性,就能知道它是最終在哪棵子樹中。對於那些落入根結點左子樹的小球來說,只需知道該小球是第幾個落在根的左子樹裏的,就可以知道它下一步往左還是往右了。依此類推,直到小球落到葉子上。 

如果使用題目中給出的編號I,則當I是奇數時,它是往左走的第(I+1)/2個小球;當I是偶數時,它是往右走的第I/2個小球。這樣,可以直接模擬最後一個小球的路線。


方法二:

#include<iostream>
#include<cstring>
using namespace std;
int main(){
    int D,I;
    while(cin>>D>>I){
        int k=1; //k是最後一個小球所走的序號
        for(int i=0;i<D-1;i++){
            if(I%2){
                k=2*k; //奇數往左走
                I=(I+1)/2; //是往左走的第(I+1)/2個
            }else{
                k=2*k+1; //偶數往右走
                I=I/2; //是往右走的第I/2個
            }
        }
        cout<<k<<endl;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章