POJ 3252 Round Numbers(数学问题)

Round Numbers 就是一个表示成二进制的时候0比1多或者相等的正数,注意是正数,所以0就肯定不是了。
     题目是给定一个区间,问在这个区间上的Round Numbers有多少个?
      首先是求出小于等于n的Round Numbers有多少个。
     我先举个例子来先说明,再来说一般方法。
 
     比如:    22 =  10110b  如果要求 <=22的Round Numbers,也就是找出1-22有多少个二进制的0不少于1的数的个数。
      22的二进制长度是5.
      首先找长度比5小的Round Numbers(长度比5小的数肯定小于22啦)
      长度为4的话,第一位必须是1,后面三位的话,可以有2个0,3个0
     所以就是C(3,2)+C(3,3);
     长度为3的Round Numbers,同理有 C(2,2);//注意不要把第一位1忘记了
     长度为2的Round Numbers,有  C(1,1)
     长度为1的Round Numbers,有 0个
 
      下面是找长度和22相同的Round Numbers。
      首先第一位是1.  
       22的第二位是0,所以第二位不能为1,必须是0
       第三位为0的话,(前面有了2个0,1个1),后面两位可以有1个0,2个0
     C(2,1)+C(2,2)
      接下来把第三位恢复为1,看第四位。假如第四位是0,(前面有2个0,2个1),后面一位必须是0    C(1,1)
 
     所以大致求的过程就如上面所述。
     首先先推个公式,就是长度为len的Round Numbers的个数。
     长度为len,第一位肯定是1了。
     那么后面剩下 len-1位。
     如果len-1是偶数。
     那么  C(len-1,(len-1)/2+1)+C(len-1,(len-1)/2+2)+````C(len-1,len-1)
=   ( 2^(len-1)-C(len-1,(len-1)/2) )/2;
    如果len是奇数
   那么就是 (  2^(len-1) )/2
   
      所以上面求比N长度小的Round Numbers是很好求的了。
 
      至于求长度的,则是逐渐把每一位1变为0,去求后面的,就可以保证比n小了。
 
    
#include<stdio.h>
#include<iostream>
using namespace std;

int C[33][33];
void init()
{
    C[0][0]=1;
    C[1][0]=1;C[1][1]=1;
    for(int i=2;i<33;i++)
    {
        C[i][0]=1;
        for(int j=1;j<i;j++)
          C[i][j]=C[i-1][j-1]+C[i-1][j];
        C[i][i]=1;
    }
}
int bits[33];
int calc(int n)//求小于等于n的 Round Numbers
{
    if(n<=1)return 0;//这个条件必须加
    int len=0;
    while(n>0)
    {
        if(n&1)bits[len++]=1;
        else bits[len++]=0;
        n>>=1;
    }
    int ans=0;
    for(int i=len-1;i>0;i--)
    {
        if(i%2==0)ans+=((1<<(i-1)))/2;
        else  ans+=((1<<(i-1))-C[i-1][(i-1)/2])/2;
    }
    int cnt0=0,cnt1=0;
    for(int i=0;i<len;i++)
    {
        if(bits[i]==0)cnt0++;
        else cnt1++;
    }
    if(cnt0>=cnt1) ans++;//n本身是 Round Number
    cnt0=0;
    cnt1=1;
    for(int i=len-2;i>=0;i--)
    {
        if(bits[i]==1)//后面有i位,第i位当成0
        {
            for(int j=i;j>=0&&j+cnt0+1>=i-j+cnt1;j--)ans+=C[i][j];
            cnt1++;
        }
        else cnt0++;
    }
    return ans;
}

int main()
{
    init();
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        printf("%d\n",calc(b)-calc(a-1));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值