题意:一个数转换成二进制数,零的个数大于等于一的个数的数称为圆数,例如9转换成1001,0的个数为2,1的个数为2,所以9为圆数;输入两个数start和end,问start到end有多少个圆数。
思路:start到end中有多少个圆数,我们只需要求出num[0,start]和num[0,end),num[0,start]-num[0,end)即为所求;
以二进制数10011000为例,位数为8,那么位数小于8的圆数有多少个?
以位数为7的情况来解释,首先保证没有前导0,这样只需要考虑剩下的6位,然后要保证零的个数大于等于一的个数,即从6个位置里面挑出大于等于一半的位置为0,组合问题不用多说了吧。枚举位数小于8的情况,再枚举为圆数的情况,求和即可;
当位数等于8时,首先我们既要保证前导零,又要保证所枚举的数不能大于本身,那么我们从第二位开始枚举每一位,记下零的个数,当枚到1时,情况就于位数小于的情况一样。一样的处理方式。详情看代码吧。
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<map>
#include<queue>
#include<cmath>
#include<stack>
#include<vector>
#include<cstdio>
#define MAXN 33000
#define INF 0x3f3f3f3f
#define lmid l,m,rt<<1
#define rmid m+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1
#define Mod 1000000007
#define i64 __int64
#define LIMIT_ULL 100000000000000000
#define Max(a,b) (a>b)?a:b
#define lowbit(x) x&(-x)
using namespace std;
typedef long long ll;
int c[33][33]={0};//c[i][j]表示从i个物品中挑j个的情况数
int bin[35];
void deal()//组合数打表
{
for(int i=0;i<=32;i++)
for(int j=0;j<=i;j++)
{
if(!j||i==j)
c[i][j]=1;
else
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
}
void dealbin(int n)//转换成二进制
{
bin[0] = 0;
while(n)
{
bin[++bin[0]]=n%2;
n/=2;
}
}
int solve(int n)
{
int sum=0;
dealbin(n);
for(int i=1;i<bin[0]-1;i++)//位数小于的情况,且保证没有前导0,实际位数为i+1
for(int j=i/2+1;j<=i;j++)//挑出j个位置为0
sum+=c[i][j];
int zero=0;
//从高位向低位搜索过程中出现0的位的个数
for (int i=bin[0]-1;i>=1;i--)
{
if (bin[i])
{
for (int j=(bin[0]+1)/2-(zero+1);j<=i-1;j++)
{
sum+=c[i - 1][j];
}
}
else zero++;
}
return sum;
}
int main()
{
deal();
int a,b;
while(scanf("%d%d",&a,&b)==2)
cout<<solve(b+1)-solve(a)<<endl;//solve(b)并没有判断b为不为圆数,所以为solve(b+1)-solve(a)
return 0;
}