蓝桥杯 二进制问题 十一届决赛

 先上代码,最好情况为log n (n的二进制位只有一个1 例如 100000000),

                    最坏情况位(log n+1)log n/2 (n的二进制位全是1 例如 11111111),

#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
ll yhsj (ll n,ll k){
    if(n<k){return 0;}
    ll res=1;
    k=k>n-k ? n-k:k;
    for (int i = 1; i <=k ; ++i) {
        res*=n-i+1;
        res/=i;
    }
    return res;
}
ll getNum(ll n,ll k){
    if(k==0){
        return 1;
    }
    if(n==0){
        return 0;
    }
    /*几位2进制 O(logn)*/
    ll index=0,temp=n;
    while (temp){
        temp/=2;
        index++;
    }
    return yhsj(index-1,k)+ getNum(n^((ll)1<<(index-1)),k-1);
}
int main(){
    ll n,k;
    cin>>n>>k;
    cout<<getNum(n,k);
}

思路:可以理解为一个对1-n的所有数二进制位进行分段计算,每次递归可减少n的二进制形态的一个1

解题流程:n=5=>n=101(2)

把n转为2进制需要log n的时间

即把问题转换为 000->101 的所有2进制中有多少个符合要求的

此时我们把大区间转换为多个小区间即000->101 转换为000 ->11+100->101

至于为何如此呢,且听我道来

我们知道只有一位的二进制有0 ,1 两位 有10 11,三位有100,101,110,111;

容易发现每种位数的二进制,是由所有比他低位的二进制加1补0而来的

即把三位二进制的最高位1去掉后得到的就是0,1,10,11 分别来自1位和二位2进制

令k=二进制位数,i为该二进制位数对应1的出现数量得到

k=Z nums[i] nums[i]代表0->Z的所有二进制中1的数量为i的二进制数的个数

易知:k=1时 nums[0]=1,nums[1]=1;

           k=2时 nums[0]=1,nums[1]=2,nums[2]=1

           k=3时 nums[0]=1,nums[1]=3,nums[2]=3,nums[3]=1

因为Z每次增加都相当于给原本存在的所有二进制每人在第一位上发一个1,所以

原本只有一个1的二进制,现在有两个,所以在nums[i]=nums[i-1]+nums[i];

其中k=z时必须要列举出所有的z位二进制才能满足上述情况

即例如k=3时必须列举完上面的4个三位二进制,否则不符合。

到这我们推出了0到某个位数的所有二进制的1数量情况

对杨辉三角熟悉可能已经发现上面列举的nums就是杨辉三角

我们可以通过二项式定理来求解某层某位上的数为多少。

现在就来分析为何如此上面的分段把其分裂

继续以n=5为例

我们现已可以求出0->11的二进制 每种1的个数的二进制个数

我们把其分解成0->11+100->101;

后面小段可以把1取出来又回到了0->1的情况我是通过递归来求解用变量记录深度即可

看了csdn很多博客 用数位dp的解法 时间复杂度较低的也O(2^logn),在n<2^5时勉强能打,之后的数据指数级和平方级还是差的太远了,且最差时才为O(log n^2),暴力的那就更加拿不上台面了

不过此解针对性强,数位dp解决的是一类问题,建议主学一下,以此为拓宽思路。

如果你听懂了且觉得思路不错,麻烦点赞支持一下,栓Q

有递归就有递归出口,递归出口就留给好兄弟自己思考一下了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值