题目链接:
思路:
1.暴力模拟:开关只有两种状态,所以可以用二进制数组模拟所有开关的状态,进而模拟每个球的下落过程(但是由于时间复杂度达到O(N*2^N,会tle)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iomanip>
#include <string.h>
#include <climits>
#include <map>
#include <stack>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxd = 20; // 球的最大数量
// 1 << maxd表示2^maxd,而二叉树结构有2^maxd-1个点,这个数组用二进制表示开关状态
int swi[1 << maxd];
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int N; cin >> N;
int D, I; // 树的深度,小球数量
for(int i=0; i<N; i++){ // 实际上要处理N+1次输入,最后一次是-1
cin >> D >> I;
memset(swi, 0, sizeof(swi)); // 开始时所有开关关闭
int k; // 当前节点位置
int n = (1 << D) - 1; // 节点数量,用来进行越界判断
while(I--){
k = 1; // 从第一个节点开始
for(;;){
swi[k] = !swi[k]; // 二进制位取反
k = swi[k] ? 2*k : 2*k+1; // 判断走的方向
if(k > n) break; // 出界
}
}
k /= 2; //返回子节点
cout << k << endl;
}
cin >> D; // 处理最后输入的-1
return 0;
}
2.思考当前小球应该往哪边走。对于任意一个开关,假如当前小球是到这里的第N个球,分两种情况考虑:
a. N是奇数,那么前N-1个球一半走左边,一半走右边,当前球必定走左边,并且它是左边开关遇到的第(N-1)/2 + 1 = (N+1)/2个球。
b. N是偶数,那么前N-1个球中N/2个走左边,N/2-1个走右边(如果不懂可以自己找几个特殊例子算一下),当前球走右边,并且是右边开关遇到的第N/2个球。
有了这个想法,我么就可以迭代地算出当前球完整的行进路线了。
ac代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iomanip>
#include <string.h>
#include <climits>
#include <map>
#include <stack>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxd = 20; // 球的最大数量
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int N;
cin >> N;
int D, I;
for(int i=0; i<N; i++){
cin >> D >> I;
int k = 1; // 当前位置
for(int i=0; i<D-1; i++){ // 这里换一种判断出界的方式:小球必定下落D-1次
if(I % 2 == 1){
k = 2*k; //I是奇数,走左边
I = (I + 1)/2; // 是左边开关的第(I+1)/2个球
} else {
k = 2*k + 1;
I = I / 2;
}
}
cout << k << endl;
}
cin >> D; // 处理最后-1的输入
return 0;
}