题目描述:
小明同学学习了不同的进制之后,拿起了一些数字做起了游戏。小明同学知道,在日常生活中我们最常用的是十进制数,而在计算机中,二进制数也很常用。现在对于一个数字x,小明同学定义出了两个函数f(x)和g(x)。
f(x)表示把x这个数用十进制写出后各个数位上的数字之和。如f(123)=1+2+3=6。
g(x)表示把x这个数用二进制写出后各个数位上的数字之和。如123的二进制表示为1111011,那么g(123)=1+1+1+1+0+1+1=6。
小明同学发现对于一些正整数x满足f(x)=g(x),他把这种数字称为幸运数,现在他想知道,小于等于n的幸运数有多少个。
输入
第一行一个整数T(T<=10000)表示数据组数,每组数据输入一个数n(n<=100000)。
输出
每组数据输出一行,小于等于n的幸运数个数。
样例输入
3
1
5
21
样例输出
1
1
3
补充:关于求二进制中1的个数。
小明同学学习了不同的进制之后,拿起了一些数字做起了游戏。小明同学知道,在日常生活中我们最常用的是十进制数,而在计算机中,二进制数也很常用。现在对于一个数字x,小明同学定义出了两个函数f(x)和g(x)。
f(x)表示把x这个数用十进制写出后各个数位上的数字之和。如f(123)=1+2+3=6。
g(x)表示把x这个数用二进制写出后各个数位上的数字之和。如123的二进制表示为1111011,那么g(123)=1+1+1+1+0+1+1=6。
小明同学发现对于一些正整数x满足f(x)=g(x),他把这种数字称为幸运数,现在他想知道,小于等于n的幸运数有多少个。
输入
第一行一个整数T(T<=10000)表示数据组数,每组数据输入一个数n(n<=100000)。
输出
每组数据输出一行,小于等于n的幸运数个数。
样例输入
3
1
5
21
样例输出
1
1
3
代码如下:
#include<iostream>
using namespace std;
void main()
{
int T;//数组的组数
cin>>T;
int *count=new int[T];
int n;
int k=0;//计数
while(k<T)
{
cin>>n;
count[k]=0;
for(int x=1;x<=n;x++)
{
int fx=0;//十进制各个数位上的数字之和
int gx=0;//二进制各个数位上的数字之和
int tmp=x;
while(tmp)
{
fx+=tmp%10;
tmp=tmp/10;
}
tmp=x;
while(tmp)
{
gx++;
tmp=tmp&(tmp-1);
}
if(fx==gx)
count[k]++;
}
k++;
}
for(int i=0;i<T;i++)
cout<<count[i]<<endl;
}
补充:关于求二进制中1的个数。
可能会想到的解法是:先判断整数二进制表示中最右边一位是不是1,接着把输入的整数右移一位,此处原来处于从右边数起的第二位被移到最右边了,再判断是不是1.这样每次移动一位,直到整个整数变为0为止。这样的解法会出现的问题是,如果输入的一个数是负数,右移时高位补1,最终这个数字会变为所有位上均为1而陷入死循环。当然,我们这里上边的关于“幸运数”的题应该不存在这个问题,输入的整数不为负数。
所以,以上解法对于负数不可行。为了避免死循环,想到我们可以不右移输入的数字。首先把输入的数字与1做与运算,判断最低位是不是1,接着把1左移一位,判断输入整数的次低位是不是1……这样反复左移,就能统计出输入整数的所有位上1的个数。代码如下:
class Solution {
public:
int NumberOf1(int n) {
int count=0;
unsigned int flag=1;
while(flag) {
if(n&flag)
count++;
flag=flag<<1;
}
return count;
}
};
另一种解法:我们还可以发现,对于一个整数减去1,都是把最右边的1变为0,如果它的右边还有0的话,所有0都变为1,而它的左边位保持不变。举一个栗子,以1100为例,减一后变为1011,将原来的数与减一后的数做与运算,得到1000,可以看出最右边1变为了0,它的左边仍保持不变,可以通过统计这样的与运算次数,来统计各个位上1的个数。利用此方法的代码如下:
class Solution {
public:
int NumberOf1(int n) {
int count=0;
while(n) {
count++;
n=n&(n-1);
}
return count;
}
};