pds*_*uza 15 algorithm binary recurrence
此问题来自2011 Codesprint(http://csfall11.interviewstreet.com/):
计算机科学的基础之一是知道数字如何以2的补码表示.想象一下,使用32位写下A和B之间的所有数字,包括2的补码表示.你会写下多少1?输入:第一行包含测试用例数T(<1000).每个下一个T行包含两个整数A和B.输出:输出T行,一行对应于每个测试用例.约束:-2 ^ 31 <= A <= B <= 2 ^ 31 - 1
样品输入:3 -2 0 -3 4 -1 4样品输出:63 99 37
说明:对于第一种情况,-2包含31 1后跟0,-1包含32 1和0包含0 1.因此,总是63.对于第二种情况,答案是31 + 31 + 32 + 0 + 1 + 1 + 2 + 1 = 99
我意识到你可以使用-X中的1的数量等于(-X)= X-1的补码中的0的数量以加速搜索的事实.该解决方案声称有一个O(log X)递归关系用于生成答案但我不明白.解决方案代码可以在这里查看:https://gist.github.com/1285119
如果有人能解释这种关系是如何产生的,我将不胜感激!
Nem*_*emo 31
好吧,这不是那个复杂...
单参数solve(int a)
函数是关键.它很短,所以我将它剪切并粘贴在这里:
long long solve(int a)
{
if(a == 0) return 0 ;
if(a % 2 == 0) return solve(a - 1) + __builtin_popcount(a) ;
return ((long long)a + 1) / 2 + 2 * solve(a / 2) ;
}
Run Code Online (Sandbox Code Playgroud)
它仅适用于非负a,它计算从0到a
包含的所有整数中的1位数.
该功能有三种情况:
a == 0
- >返回0.显然.
a
even - >返回a
加1的1位数solve(a-1)
.也很明显.
最后一个案例是有趣的.那么,我们如何计算从0到奇数的1位数a
?
考虑0和之间的所有整数a
,并将它们分成两组:平均值和赔率.例如,如果a
是5,则您有两个组(二进制):
000 (aka. 0)
010 (aka. 2)
100 (aka. 4)
Run Code Online (Sandbox Code Playgroud)
和
001 (aka 1)
011 (aka 3)
101 (aka 5)
Run Code Online (Sandbox Code Playgroud)
请注意,这两个组必须具有相同的大小(因为它a
是奇数,范围是包含的).要计算每组中有多少1位,首先计算除最后位之外的所有位,然后计算最后位.
除最后一位之外的所有位都是这样的:
00
01
10
Run Code Online (Sandbox Code Playgroud)
......对于这两个群体来说,它看起来都像这样.这里的1位数就是solve(a/2)
.(在这个例子中,它是从0到2的1位数.另外,回想一下C/C++中的整数除法向下舍入.)
对于第一组中的每个数字,最后一位为零,对于第二组中的每个数字,最后一位为0,因此最后(a+1)/2
一位对总数贡献一位.
因此,递归的第三种情况是(a+1)/2 + 2*solve(a/2)
,用适当的强制转换成long long
处理其中的情况下a
是INT_MAX
(并因此a+1
溢出).
这是一个O(log N)解决方案.要概括它solve(a,b)
,你只需计算solve(b) - solve(a)
,加上适当的逻辑来担心负数.这就是两个论点solve(int a, int b)
正在做的事情.
归档时间: |
|
查看次数: |
6002 次 |
最近记录: |