二进制问题

​​​​​​想要了解更多相关题,请点击

题目描述

小蓝最近在学习二进制。他想知道 1 到 N 中有多少个数满足其二进制表示中恰好有 K 个 1。你能帮助他吗?

输入描述

输入一行包含两个整数 N 和 K。

输出描述

输出一个整数表示答案。

输入输出样例

示例

输入

7 2

输出

3

评测用例规模与约定

对于 30% 的评测用例,1≤N≤1e6,1≤K≤10。

对于 60% 的评测用例,1≤N≤2×1e9,1≤K≤30。

对于所有评测用例,1≤N≤1e18,1≤K≤50。

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

思路:

首先我们肯定要先把n转为二进制

nums=[]
while n:#
    nums.append(n%2)
    n//=2

我们可以想到,假如将n转为二进制后有m位,那么此时是不是使用组合就可以了?|
例如:m=5,k=2。那么答案会是C(5,2)=10吗? 
其实不是的,比如n=17,转二进制为【10001】是五位,但是对于【11000】【10100】....之类的
虽然也是五位,但是其大小已经超过了n!!!,注意我们求的是1~n

此时我们就要分情况讨论了,我们可以从高向低位看,若当前位为1,有两种情况:
        1.填0,那么就要在后面找够剩下的1,此时就可以使用组合了,因为当前位本是1,现在填了0,那么后面无论怎么填都不会大于n
        例如:n转二进制为【11001】,若当前遍历到下标1也就是第二个1,那么如果将当前填0,后面的两个1无论怎么填都不为过,比如【10101】【10011】...之类的
        2.填1,那么此时剩余的1就少了一个,因为我们用了一个1

其实学过数位dp的朋友就能知道这是一个数位dp题。

                

代码实现:

f=[[0]*(62) for i in range(62)]#[位数][1数]:方案数量
for i in range(62):
    f[i][0]=1
    f[i][i]=1
    for j in range(1,i):
        f[i][j]=f[i-1][j]+f[i-1][j-1]

def dp(n,k):#求1到n有几个满足有k个1
    if n==0:
        return 0 
    n1=n
    nums=[]
    while n:#拆分
        nums.append(n%2)
        n//=2
    if len(nums)<k:#不够k位
        return 0
    if len(nums)==k:
        return 1 if n1==((2**k)-1) else 0#n转为二进制整好k个1?

    last=0#出现了几个1
    ans=0#答案
    for i in range(len(nums)-1,-1,-1):#高到低
        x=nums[i]
        if x:#为1
            ans+=f[i][k-last]#i位实际是低一位,若填0,剩下的1在后面补齐
            last+=1#发现一个1

        if i==0 and last==k:#其本身也有可能符合条件
            ans+=1
    return ans

n,k=map(int,input().split())

print(dp(n,k))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自 在

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值