小球下落
有一顆二叉樹,最大深度爲D,且所有葉子的深度都相同,所有結點從上到下從左到右編號爲1,2,3,…,2 ^ D - 1.在結點1處放一個小球,它會往下落。每個內結點上都有一個開關,初始全部關閉,當每次有小球落到一個開關上時,狀態都會改變。當小球到達一個內結點時,如果該結點上的開關關閉,則往左走,否則往右走,直到走到葉子結點。
一些小球從結點1處一次開始下落,最後一個小球將會落到哪裏?輸入葉子深度D和小球個數I,輸出第I個小球最後所在的葉子編號。假設I不超過整棵樹的葉子個數。D<= 20.輸入最多包含1000組數據。
樣例輸入
4 2
3 4
10 1
2 2
2 128
16 12345
樣例輸出:
12
7
512
3
255
36358
數組模擬的方法:運算量大
#include<iostream>
#include<cstring>
using namespace std;
const int N = 20;
int s[1 << N];//二進制下,每乘一個2就向左進一位,1左移N就相當於1 x 2 ^ N;這裏存最大結點數
int main()
{
int D, I;//葉子深度和小球個數
while(~ scanf("%d%d", &D, &I)){
memset(s, 0, sizeof(s));//開關初始爲關閉
int k, n = (1 << D) - 1; //n是最大結點編號 2 ^ D - 1;
for(int i = 0; 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 個小球。
模擬最後一個小球路線
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int D, I;//葉子深度和小球個數
while(~ scanf("%d%d", &D, &I))
{
int k = 1;
for(int i = 0; i < D - 1; i ++ )
{
if(I % 2 != 0)//奇數個數的球路線
{
k = k * 2;
I = (I + 1) / 2;
}
else//偶數個數的球路線
{
k = k * 2 + 1;
I /= 2;
}
}
printf("%d\n", k);
}
return 0;
}