n, k = map(int, input().split())
bstr = str(bin(n))[2:]
def Cn(m, n):
# 这个判断代表:当剩余二进制位数小于所需k值,即就算剩下的所有位都置1,依然无法满足条件
if n < m:
return 0
# A计算
a = 1
for i in range(n, (n - m), -1):
a *= i
# 阶乘计算
t = 1
for i in range(1, m + 1):
t *= i
# C计算
return a / t
cnt = 0
for i in range(0, len(bstr)):
if k == 0:
cnt += 1
break
if bstr[i] == '1':
plus = int(Cn(k, len(bstr) - i - 1))
# 下面这步判断代表:当所需k值<=剩余二进制位数时,是直接退出或是cnt+1退出
if plus == 0:
# 当二进制数最后一位正好==k值==1时(相当于输入 1 1 特殊情况)
if int(bstr[i]) == k:
cnt += 1
break
cnt += plus
k -= 1
print(cnt, end='')
在使用python的情况下速度依然达到了159ms,案例也全部AC
思路
-
首先想到的是遍历所有可能出现的排列组合情况,但从题目给的数值范围来看肯定会TLE(超时),这种思路不符合实际情况。
-
但是思路1的方向是正确的,我们知道傻傻的遍历肯定不行,只有聪明的遍历才能拿到AC,这也是剪枝的思路。
-
于是我随便拿一个数12345,观察他的二进制形式 11000000111001,初步可以想到,拿前五位来说,人类一眼就能看出来11000肯定比11100小,可以直接删掉11100—之类的组合。嗯~~~~,思路来了。
-
那这么说,我们可以从二进制的高位(即从左到右的顺序)开始,一步步往低位进发。从思路3可以看出,如果原二进制数org_b的某一位是1,排列组合出来的新数new_b的某一位也是1,那情况会比较复杂,需要与原二进制数比较并逐级向低位判断,但如果是new_b的这一位是0呢,不管后面二进制位怎么置1,都不可能比我现在大,所以这时候直接使用排列组合C(k,n)就行,n就是当前二进制位之后的位数。
-
从思路4可以看出来,这就是循环判断的过程。如果org_b的一位是1,那么就进行思路4的判断,如果是0,则直接跳过(因为二进制位当中,没有比0更小的数了,锁死了new_b的选择)。
-
选择了一个1,就将k值-1,直到最后k值归零,输出cnt即可(也有可能会存在剩余位数小于k值或正好等于k值,cnt+0或+1即可)
至此,流程结束。作为蓝桥杯决赛倒数第二题,有一定的难度和灵活度,但不需要用到dp还是挺友好的



