题:二进制问题
题目大意: 求1 到 N 中有多少个数满足其二进制表示中恰好有 K 个 1
题目分析:先求出一个dp数组,dp[i][j]表示前i位有j个1的情况总数,从最高位往最低位看,如果一个位是1,那么这一位上可以选择1或者是0,如果是0的话,那么这个位不能选。如果是1,选择0的话,那么ans+dp[i-1][k-last],k是总数,last是已经选过的1,可以发现此时算的方案是在前面last位都选1,而本位选0.选择1的话,那么last++,不做其他。其正交性可以证明。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
//设dp[i][j]表示从[0,i]位中共有j个1
long long dp[100][100];
typedef long long ll;
void init()
{
dp[0][0] = 1;
dp[0][1] = 1;
for (int i = 1; i <= 100; i++)
{
dp[i][0] = 1;
for (int j = 1; j <= i+1; j++)
{
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
}
}
}
long long solve(ll n, int k)
{
vector<int> num(100, 0);
int s = 0;
while (n)
{
if (n % 2 == 1)
{
num[s++] = 1;
}
else
num[s++] = 0;
n = n / 2;
}
ll ans = 0;
int last = 0;
for (int i = s - 1; i >= 1; i--)
{
int x = num[i];
if (x)
{
ans = ans + dp[i - 1][k - last];//本位选0
last++;//本位选1
if (last > k)
break;
}
}
if(num[0]==1&&last==k-1)
ans++;
if (last == k)
ans++;
return ans;
}
int main(void)
{
//dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
ll n;
int k;
scanf("%lld%d", &n, &k);
init();
ll res=solve(n, k);
printf("%lld", res);
}