poj3252组合数学

题意:一个数转换成二进制数,零的个数大于等于一的个数的数称为圆数,例如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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值