AcWing 801.二进制中1的个数

AcWing 801.二进制中1的个数


<二进制>


核心:

lowbit(x)函数


简介:

**此题解重点在于详细解释了lowbit函数 ** 也只有这个需要仔细解释了叭

ps: 如看完注释还是不太理解的读者 可以再康康代码下新加的两点补充


C++ 代码 + 详细注释:

#include <iostream>

using namespace std;

int n;

 /* lowbit()函数 作用:返回 x 的最后一位 1 (二进制的形式下)
 	    应用:也是树状数组常用操作之一 
 	
	 实现原理 :-x = ~x + 1;
	   而 ~x 后 最后一个1后取反为0,其后的所有0(如果有的话)都变为1
	  故再 +1 后 后面所有取反后的 1 (如果没有则忽略此步骤)会从最后一个1开始向前进位且该位的数变为 0(即恢复原位)
			直至进位到原数最后一个 1 取反后的 0 处停止向前进位。(也相当于恢复原位)
		“ps:即使后面没有0也不会影响最后结果,只是相当于省掉了这样一个后面的数进位恢复原位的过程”
		
	至此 :从原数最后一个 1 一直到最后的位置上的数每个位置上的数都恢复到了原来的数,
				而原数最后一个 1 之前的所有数都仍然保留取反后的情况 
					(由上面进位过程得:进位到原数最后一个1的位置就停下进位了)
					
	所以最后 x & -x 的值就是 x 的最后一位1所形成的值
  (因为由上述分析得知 ~x + 1 后的数除了原数最后一个1的位置和原数一样都为1,其余的1的位置全都保留取反后的情况,即为0)
  			(而 -x = ~x + 1, 所以 x & -x = x & (~x + 1), 按位与之后就得到了最后一位1的值)
 						*/
 						
int lowbit(int x) {
	return x & -x;
}

int main() {
	cin >> n;
	while (n -- ) {
		int x, ans = 0;
		cin >> x;
		while (x) x -= lowbit(x), ans ++ ; 
	  // x 每次减去二进制中最后一个 1,故循环结束后减去的次数就为 x 的二进制中 1 的个数 
	  	cout << ans << ' ';
	}
	cout << endl;
	
	return 0;
}

补充:

想了想如果只是单纯的文字解释可能还是有点抽象
于是决定再加上例子补充啦 ~
原始版写的时候我还是第一次写题解(就很简陋也单调 见谅哈~) 所以这次决定加上补充~

咳咳,正文在此:

1、-x = ~x + 1的说明:

1)、从数学角度:

​ 对于一个数x和它的相反数-x,那么我们应该都知道它们俩应该满足条件x + (-x) = 0

​ 故对于二进制形式下的x来说它也应当满足 x + (-x) = 0...0 (…表示多个)

eg:x = 10

则 x 的二进制形式为: ( 1010 ) 2 (1010 )_2 (1010)2 故需满足 ( 1010 ) 2 (1010)_2 (1010)2 + ( − x ) 2 (-x)_2 (x)2 = ( 0000 ) 2 (0000)_2 (0000)2

所以: ( − x ) 2 (-x)_2 (x)2 = ( 0000 ) 2 (0000)_2 (0000)2 - ( 1010 ) 2 (1010)_2 (1010)2
​ = ( 0110 ) 2 (0110)_2 (0110)2
而 ~x = ( 0101 ) 2 (0101)_2 (0101)2 ,~x + 1 = ( 0110 ) 2 (0110)_2 (0110)2 = ( − x ) 2 (-x)_2 (x)2

所以 -x = ~x + 1

2)、从计算机内部存储整数的方式角度:

这个角度就更简单粗暴地好理解啦

​ 计算机用补码存储一个整数,而正数的补码是它本身,负数的补码是正数的补码取反 + 1

2、 lowbit(x)函数的例子演示:

eg: x = 10
则 x 的二进制形式为: ( 1010 ) 2 (1010 )_2 (1010)2 --> 故x的最后一位1应为: ( 10 ) 2 (10)_2 (10)2 ,即 2
取反: ~x = ( 0101 ) 2 (0101)_2 (0101)2
再 +1 : ~x + 1 = ( 0110 ) 2 (0110)_2 (0110)2

而此时 lowbit(x)

​ = x & -x

​ = x & (~x + 1)

​ = ( 1010 ) 2 (1010)_2 (1010)2 & ( 0110 ) 2 (0110)_2 (0110)2

​ = ( 0010 ) 2 (0010)_2 (0010)2

​ = 2

故做法正确~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值