题目描述
小 P 在学习了位运算后,定义了如下"平衡数":
- 对于一个正整数 aa,如果其二进制表示中
1和0的个数相等,则称其为平衡数。
这里的二进制表示以 1 为最高位,即不考虑更高位用于补位的 0。例如,正整数 12 的二进制表示为 1100,包含两个 1 和两个 0。在实际存储中,更高位可能需要填 0 补位,如 00001100,在本题中无需考虑此种情况。
小 P 已经生成好了 n 个正整数 a1,a2,⋯,an,试编程统计里面有多少个平衡数。
输入格式
从标准输入读入数据。
输入的第一行包含一个正整数 n,第二行包含空格分隔的 n 个正整数 a1,a2,⋯,an。
输出格式
输出到标准输出。
输出一个整数,表示 a1,a2,⋯,an中平衡数的个数。
样例输入
3
2 5 9
样例输出
2
样例解释
2 的二进制表示为 10,是平衡数;
5 的二进制表示为 101,不是平衡数;
9 的二进制表示为 1001,是平衡数。
子任务
50% 的测试点满足:0<ai≤256;
全部的测试点满足:0<n≤100 且 0<ai≤109。
题解
判断数字是否为平衡数的方法:首先将数字转换为二进制,然后统计其中0和1的个数是否相等。以下是具体实现方案:
二进制转换方法
将十进制数转换为二进制数的步骤如下:
- 对原数进行模2运算
- 将结果从低位到高位依次记录
- 将所得数值倒序排列即为对应的二进制数
在实际操作中,我们无需获取完整的二进制表示,只需统计位数即可。这里使用临时变量temp:
- 当模2结果为1时加1
- 否则减1
- 最终判断temp是否为0
位运算优化方案
采用异或(XOR)运算进行优化,通过五轮"对折"异或将32位整数的所有位信息压缩到最低位:
| 步骤 | 操作 | 效果 |
|---|---|---|
| 1 | x ^= x >> 16 |
高16位与低16位异或 → 结果在低16位 |
| 2 | x ^= x >> 8 |
高8位与低8位异或 → 结果在低8位 |
| 3 | x ^= x >> 4 |
高4位与低4位异或 → 结果在低4位 |
| 4 | x ^= x >> 2 |
高2位与低2位异或 → 结果在低2位 |
| 5 | x ^= x >> 1 |
高1位与低1位异或 → 结果在最低位 |
最终通过!(x & 1)判断原始数字中1的个数是否为偶数。
示例演示
以x = 13为例(二进制:1101,3个1):
初始: x = 0000 0000 0000 0000 0000 0000 0000 1101
第1轮: x ^ (x >> 16) → x不变
第2轮: x ^ (x >> 8) → x不变
第3轮: x ^ (x >> 4) → x不变
第4轮: x ^ (x >> 2) → x = ...0000 1110 (14)
第5轮: x ^ (x >> 1) → x = ...0000 1001 (9)
最终: !(x & 1) = false
代码如下
cpp
#include<bits/stdc++.h>
using namespace std;
int cnt,n,op;
bool check(int x){
int temp=0;
while(x){
if(x%2)temp++;
else temp--;
x/=2;
}
return temp==0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>op;
if(check(op))cnt++;
}
cout<<cnt;
}
给一个位运算优化的方法
cpp
#include<bits/stdc++.h>
using namespace std;
int cnt,n,op;
bool check(int x){
cout<<sizeof(x)<<endl;
x=x^x>>16;
x=x^x>>8;
x=x^x>>4;
x=x^x>>2;
x=x^x>>1;
return !(x&1);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>op;
if(check(op))cnt++;
}
cout<<cnt;
}