题目大意:
有一棵二叉树, 最大深度为D, 且所有叶子的深度都相同, 所有节点从上到下,从左到右编号依次为, 0, 1, 2, ....(2^n)-1,从节点1开始放球, 每个节点有一个开关, 开关关时向左 ,否则向右, 小球的每次撞击会使开关的状态改变。
这是一道很蠢的题, 一个简单的模拟就会输出答案:
#include <iostream> #include <cstdio> #include <string> #include <cstring> using namespace std; const int maxd = 20; int s[1 << maxd]; int main() { int D, I; while( ~scanf( "%d%d", &D, &I ) ) { memset( s, 0, sizeof( s ) ); int k = 0; int n = ( 1 << D ) - 1; for( int i = 0; i < I; i++ ) { k = 1; while( 1 ) { s[k] = !s[k]; k = s[k] ? k * 2 : k * 2 + 1; if( k > n ) break; } } printf( "%d\n", k / 2 ); } return 0; }
但是时间会超。
所以不知道哪一位大佬, 很聪明的发现:
单对于第一个小球来说, 会一直向左走( 因为初始状态下, 开关全部关闭 ), 对于第二个小球来说会落到右子树的最左节点( 因为第一个球将根节点的开关状态改变 ), 同理第三球会落到左子树的右子树的最左节点......
所以发现规律:
当最后一个球为奇数的时候会走到左子树, 这时将左子树的第一个节点当做根节点, 迭代下去直到当前的深度deep超过给定的深度D
思维题。
哪一道题都需要自己仔细的斟酌。
#include <iostream> #include <cstdio> using namespace std; int main() { int d, i; while( ~scanf( "%d%d", &d, &i) ) { int ans = 1; int deep = 1; while( deep < d ) { if( i & 1 ) { ans *= 2; i = ( i + 1 ) / 2; } else { ans = ans * 2 + 1; i /= 2; } deep++; } printf( "%d\n", ans ); } return 0; }
坚持早起已经一周多了, 感觉还不错, 另外一会儿把周日那场组队赛的题单的博客写了。
多练思维 ,算法只是辅助的工具, 怒整动态规划!