小球下落,紫书P148UVa679

来源:互联网 发布:linux vim 编辑:程序博客网 时间:2024/04/30 14:34

这是一道我非常喜欢的算法题目。本题的技巧性非常高。虽然是一道依据于满二叉树题目,但是单纯的模拟题目小球下落的程序,也许能过几个小范围测试数据,数据量太大的时候必定TL。即使是模拟程序,刘也依然写的非常精简。其中k = s[k] ? k*2 : k*2+1;这个语句很值得c++不熟练的选手学习。

#include <cstdio>#include <cstring> const int maxd 20;int s[1<<maxd];int main(){    int D,I;    while (scanf("%d%d", &D, &I) == 2){        memset(s, 0, sizeof(s));        int k, n = (1<<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;}                          

由上面程序可知,当数据量很大的时候,是一定会超时的。那么真正高效率的算法是什么?
对于每一棵树而言,落到其根节点的第奇数个小球都落入其左子树,第偶数个小球都落入其右子树。而落入其左子树的小球n,是第(n+1)/2个落入其左子树的。落入右侧的同理。对于他的子树也按照此规律类推,即可根据最后一个小球的编号直接递推出其最终落在根节点的位置,省去了普通算法中前n-1次的复杂运算,大大降低了时间复杂度。

// UVa679 Dropping Balls// Rujia Liu#include<cstdio>int main() {  int T, D, I;  scanf("%d", &T);  while(T--) {    scanf("%d%d", &D, &I);    int k = 1;    for(int i = 0; i < D-1; i++)      if(I%2) { k = k*2; I = (I+1)/2; }      else { k = k*2+1; I /= 2; }    printf("%d\n", k);  }  return 0;}

哈哈,正解算法程序算上注释才刚刚16行。实在是非常有趣。