当寒月还在读大一的时候,他在一本武林秘籍中(据后来考证,估计是计算机基础,狂汗-ing),发现了神奇的二进制数。
如果一个正整数m表示成二进制,它的位数为n(不包含前导0),寒月称它为一个n二进制数。所有的n二进制数中,1的总个数被称为n对应的月之数。
例如,3二进制数总共有4个,分别是4(100)、5(101)、6(110)、7(111),他们中1的个数一共是1+2+2+3=8,所以3对应的月之数就是8。
Input给你一个整数T,表示输入数据的组数,接下来有T行,每行包含一个正整数 n(1<=n<=20)。
如果一个正整数m表示成二进制,它的位数为n(不包含前导0),寒月称它为一个n二进制数。所有的n二进制数中,1的总个数被称为n对应的月之数。
例如,3二进制数总共有4个,分别是4(100)、5(101)、6(110)、7(111),他们中1的个数一共是1+2+2+3=8,所以3对应的月之数就是8。
Output对于每个n ,在一行内输出n对应的月之数。
Sample Input
3 1 2 3Sample Output
1 3 8
解法一:递推
仔细观察递推过程,会发现
1位2进制:数的个数=1=2^0 f(1)=1
2位2进制:数的个数=2=2^1 f(2)=3=2*f(1)+2^0
3位2进制:数的个数=4=2^2 f(2)=8=2*f(2)+2^1
4位2进制:数的个数=8=2^3 f(2)=8=2*f(3)+2^2……
每一个2进制中1的个数都是在上一个2进制的每个数基础上在最右边加上1或0,所以1的个数先是上一个二进制中个数的二倍,然后如果加1,1的个数会增加,加0不会
#include<stdio.h> #include<math.h> int f[21]; void set() { int i; f[1]=1; for(i=2;i<=20;i++){ f[i]=2*f[i-1]+pow(2,i-2); } } int main(void){ int n,val; set(); scanf("%d",&n); while(n--){ scanf("%d",&val); printf("%d\n",f[val]); } return 0; }
解法二:用数学公式(讲解摘自老师博客)
该问题的另外一种解法是用组合数学的知识直接计算n二进制数中的1的个数。
对于输入的n,n二进制数即n位并且首位为1的二进制数,满足:
pow(2,n-1) ≤ n二进制数 < pow(2,n)
因为首位为1,n二进制数的个数就是n-1位的0和1的组合数,即pow(2,n-1)个。
第1位必须为1,所以第1位的1的个数为pow(2,n-1)个。
其他n-1位,总位数为(n-1)*pow(2,n-1)。其中0和1的个数是一半对一半,所以1的位数为(n-1)*pow(2,n-1)/2。
合计1的位数为:pow(2,n-1) +(n-1)*pow(2,n-1)/2。
实际计算时,没有必要用数学函数pow来计算。用移位运算来计算2的n次方是一种快速的计算方法。
即1<<(n-1)+(n-1)*(1<<(n-2))
解决问题的第一步用数学方法是十分有效的;第二步则需要用一些程序技巧来简化计算。