题目大意:
小球从一棵所有叶子深度相同的二叉树的顶点开始向下落,树开始所有节点都为0。若小球落到节点为0的则往左落,否则向右落。并且小球会改变它经过的节点,0变1,1变0。给定树的深度D和球的个数I,问第I个小球会最终落到哪个叶子节点。
解题思路:
完全二叉树有一个重要的性质:对于任意一个节点k,其左节点、右节点的编号分别为2k和2k+1
对于根节点,很容易知道当球的编号为奇数时,球落入左子树,偶数时落在右子树。这样其实对于其它节点看成根节点时也是一样的。比如对于第7个球,为奇数,是第7个到达一号节点的球,也是第(7/2)+1=4个到达2号节点球,再往下也是第2个到达5号节点的球。。。。
所以可以直接通过找规律,模拟最后一个球的判断过程
这是我的AC代码
#include <cstdio> typedef long long ll; ll d, n; int main() { int cur; while (scanf("%d", &cur) != EOF,cur!=-1) { while (cur--) { scanf("%lld%lld", &d, &n); int height = 1; ll num = 1; while (height < d) //从深度为1的根节点到达深度为d的叶子总共需要判断d-1次 { if (n % 2 == 0) //当球的编号为偶数的时候 { num = num * 2 + 1; n = n / 2; height++; } else //当球的编号为奇数的时候 { num = num * 2; n = (n + 1) / 2; height++; } } printf("%lld ", num); } } return 0; }
以下更为精简的代码,和两种思路
数学思路
#include<stdio.h> int main() { int D, I,cas; while (scanf("%d", &cas) != EOF,cas!=-1) { while (cas--) { scanf("%d%d", &D, &I); int k = 1; for (int i = 0; i < D - 1; i++) { if (I % 2) //奇数 { k = 2 * k; I = (I + 1) / 2; } else { k = 2 * k + 1; I = I / 2; } } printf("%d ", k); } } return 0; }
模拟下落过程
#include<stdio.h> #include<string.h> const int MAXD = 20; int s[1<<MAXD]; //最大节点个数 2^MAXD - 1 int main() { int D, I,cas; while (scanf("%d", &cas) != EOF,cas!=-1) { while (cas--) { scanf("%d%d", &D, &I); memset(s, 0, sizeof(s)); //开关初始全0,表示关闭 int k, n = (1 << D) - 1; //k表示下落到的节点的开关下标 for (int i = 0; i < I; i++) //共下落I个小球 { k = 1; while (1) { s[k] = !s[k]; k = s[k] ? 2 * k : 2 * k + 1; //根据开关选择左右子树 if (k > n) break; //落到树外,出界 } } printf("%d ", k / 2); //出界前叶子节点编号 } } return 0; }
2018-03-31