解题思路:

对于二进制转十进制来说,有一个简单的方法是8 4 2 1

以101为例,它转成十进制就是1*2^(3-1)+0*2^(2-1)+1*2^(1-1) 即为4+0+1=5

我们可以发现它可以拆成多少个2的整数次方,就有多少个1。

5(101)可以拆成4和1,有两个,所以就是两个1,7(111)可以拆成4+2+1,所以就是三个1。

考虑到2的三十次方以上就超过int型的最大数了,所以我们把2的30以内的次方以打表的形式放进数组中,减少运算。

参考代码:

#include <stdio.h>

long long int p[31]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,};

int main ()
{
	int i,j,k=0;
	int n=0,m=0,z=0;
	scanf("%d %d",&n,&m);
	for(i=n;i<=m;i++){
		k=i;
		while(k>0){
			for(j=0;j<31;j++){
				if(p[j]<=k&&p[j+1]>k){//找到刚好比k小的2的整数次方
					break;
				}
			}
			k-=p[j];//减去
			z++;//1的数量加1
		}
	}
	printf("%d",z);

	
	return 0;
}

 第二个方法是&运算。

&运算中1&1=1,1&0=0,0&0=0

通过n=n&(n-1),n通过几步达到0,n的二进制就有几个0

以7(111)为例

111&110=110 一次

110&101=100 两次

100&011=000 三次,最后正好到0。

代码实现如下,具体位运算的巧妙还需要大家多了解位运算的知识:

#include <stdio.h>

int main ()
{
	int i,j,k=0;
	int n=0,m=0,z=0;
	scanf("%d %d",&n,&m);
	for(i=n;i<=m;i++){
		k=i;
		while(k>0){
			k=k&(k-1);
			z++;
		}
	}
	printf("%d",z);

	
	return 0;
}


点赞(0)
 

0.0分

2 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论