先上代码,最好情况为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
有递归就有递归出口,递归出口就留给好兄弟自己思考一下了。