題目鏈接: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;
}