题目地址:
https://www.acwing.com/problem/content/description/803/
给定一个长 n n n的数列 A A A,求每个数的二进制表示中 1 1 1的个数。
输入格式:
第一行包含整数
n
n
n。第二行包含
n
n
n个整数,表示整个数列。
输出格式:
共一行,包含
n
n
n个整数,其中的第
i
i
i个数表示数列中的第
i
i
i个数的二进制表示中
1
1
1的个数。
数据范围:
1
≤
n
≤
100000
1\le n\le 100000
1≤n≤100000
0
≤
A
[
i
]
≤
1
0
9
0\le A[i]\le 10^9
0≤A[i]≤109
可以用lowbit来做。代码如下:
#include <iostream>
using namespace std;
int lowbit(int x) {
return x & -x;
}
int count1(int x) {
int res = 0;
while (x) {
x -= lowbit(x);
res++;
}
return res;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
printf("%d ", count1(x));
}
return 0;
}
时间复杂度 O ( ∑ i log A [ i ] ) O(\sum_i \log A[i]) O(∑ilogA[i]),空间 O ( 1 ) O(1) O(1)。
最优的做法可以做到
O
(
1
)
O(1)
O(1)复杂度求
x
x
x的二进制
1
1
1的个数。这个做法是利用了倍增的思想。先将
x
x
x的二进制位两两分组,考虑二进制数0101...01 = 0x55555555
,那么(x & 0x55555555) + (x >> 1 & 0x55555555)
这个数里,每两位表示的值就是原数中每两位里
1
1
1的个数。如图所示:
令x = (x & 0x55555555) + (x >> 1 & 0x55555555)
,接下来将
x
x
x四个四个分组,考虑二进制数00110011...0011 = 0x33333333
,那么此时(x & 0x33333333) + (x >> 2 & 0x33333333)
这个数每四位表示的值就是原数中每八位中
1
1
1的个数;再令x = (x & 0x33333333) + (x >> 2 & 0x33333333)
,接下来将
x
x
x八个八个分组,考虑二进制数0000111100001111...00001111 = 0x0f0f0f0f
,那么此时(x & 0x00FF00FF) + (x >> 8 & 0x00FF00FF)
这个数每八位表示的值就是原数中每八位中
1
1
1的个数;以此类推,倍增到三十二位分组的时候(此时只有一个组了),就得到了原数中
1
1
1的个数了。代码如下:
#include <iostream>
using namespace std;
int count1(int x){
x = (x & 0x55555555) + (x >> 1 & 0x55555555);
x = (x & 0x33333333) + (x >> 2 & 0x33333333);
x = (x & 0x0F0F0F0F) + (x >> 4 & 0x0F0F0F0F);
x = (x & 0x00FF00FF) + (x >> 8 & 0x00FF00FF);
x = (x & 0x0000FFFF) + (x >> 16 & 0x0000FFFF);
return x;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
printf("%d ", count1(x));
}
return 0;
}
时间复杂度 O ( n ) O(n) O(n),空间 O ( 1 ) O(1) O(1)。