小球下落-二叉樹

kewlgrl發表於2015-08-18

有一顆二叉樹,最大深度為D,所有葉子的深度都相同。所有結點從上到下從左到右的編號分別依次是1,2,3,4,~,(2的D次方-1)。在節點1放下一個小球,它會往下落。每個內結點都有一個狀態(開或關),初始時,每個內結點都處於關閉狀態,當小球經過一個內結點時,開關狀態會改變。當為開狀態時,小球向左落下;當為關狀態時,小球向下落下,直到走到葉子結點。

輸出樹的深度D,和小球數量I

輸出第I個小球落到的結點編號

輸入樣例: 

4 2

3 4

2 2

16 12345

輸出樣例:

12

7

3

36358

模擬全過程:

#include<stdio.h>
#include<string.h>
const int maxn=20;
int s[1<<maxn];   //最大節點個數為2^maxn-1
int main()
{
    int D,I;
    while(scanf("%d%d",&D,&I)==2)
    {
        memset(s,0,sizeof(s));     //開關
        int k,n=(1<<D)-1;    //n是最大節點編號
        for(int i=0; i<I; i++) //連續讓I個球下落
        {
            k=1;
            for(;;)
            {
                s[k]=!s[k];
                k=s[k]?k*2:k*2+1;    //根據開關狀態選擇下落方向
                if(k>n)         //已經落“出界”了
                    break;
            }
        }
        printf("%d\n",k/2);    //出界之前的葉子編號
    }
    return 0;
}

找規律:

當I是奇數時,它是往左走的第(I+1)/2個小球;當I是偶數時,她是往右走的第I/2個小球。這樣,可以直接模擬最後一個小球的路線:

如果I是奇數,它一定會落在左子樹,否則,一定是在右子樹。以I為3為例,可以將它看作是從節點2開始降落的第二個球,依此類推。

#include<stdio.h>
int main()
{
    int D,I;
    while(scanf("%d%d",&D,&I)==2)
    {
        int k=1;
        for(int i=0; i<D-1; i++)
            if(I%2)
            {
                k=2*k;
                I=(I+1)/2;
            }
            else
            {
                k=k*2+1;
                I/=2;
            }
        printf("%d\n",k);
    }
    return 0;
}

From:《演算法競賽入門經典》

相關文章