POJ 3252 Round Numbers

    好久不写,这个题目又弄了几个小时,水平还是有限。

   这道题我用组合数学来做,如果要求区间[a,b]的个数,先求出[2,a)的个数和[2,b]的个数,两者相减。

    首先我们假设求 [1000,10000)(二进制表示)这个区间,那么在这个区间里高位都是为1的,剩下3位里面必须至少有两位为0。故为C(3,2)+C(3,3)=C(3,0)+C(3,1),因为左边和右边之和为2^3(二项式定理),故解为2^2。

     那如果是[10000,100000),同理可以得到有C(4,2)+C(4,3)+C(4,4),因为 C(3,0)+C(3,1)+C(4,2)+C(4,3)+C(4,4)=2^4,所以前面那个式子的和为(2^4-C(4,2))/2.由上面可以分为奇偶情况来分别计算。

    再假设要求[2,20)区间的个数,20=10111,那么我们先求出[10,10000)(二进制)这个就是上面讨论的情况了。然后再取为1的情况10011,右边第二位为1,如果为0,则有1000**,后面两位可以随意取,然后根据1/0的情况像上面一样讨论。然后再后移找为1的情况,直到完成。

代码:

#include<iostream>
#include<cmath>
using namespace std;
int dp[35][35], sqr[35];
void Cn()  //二的幂,组合 
{
     int i,j;
     memset(dp,0,sizeof(dp));
     dp[0][0]=1;
     for( i=1;i<=32;i++)
      for( j=0;j<=i;j++){
          if(j==0) dp[i][j]=1;
          else dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
      }
     sqr[0]=1;
     for(i=1;i<=31;i++)
       sqr[i]=sqr[i-1]*2;
}

int Fn(int k,int d)  // k个数,原来0比1多d个 
{
    if(k-d<=0) return sqr[k]; 
    
    int sum=0,i;
    if((k-d)%2) i=(k-d+1)/2;
    else i=(k-d)/2;
    for( ;i<=k;i++)
         sum+=dp[k][i];
    return sum;
}

void Cal(int n,int&sum)
{
    int temp,num[35],i,j;
    int one=0,zero=0;
    
    temp=n;
    for( i=0;temp;i++){
           if( temp&1)  num[i]=1;
           else   num[i]=0;
           temp>>=1;
    }
 
    for( j=1;j<i-1;j++){ //i是二进制的长度。 
         if(j%2) sum+=sqr[j-1];      //奇偶的情况分析
       else sum+=(sqr[j]-dp[j][j/2])/2;
    }  
    
    one=0,zero=0;
    for( j=i-2;j>=0;j--){
         if( num[j]){ 
             sum+=Fn(j,zero-one);
             one++;
         }        
         else zero++;
    }  
    if(one+1<=zero) sum++;                            
}

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


 

你可能感兴趣的:(POJ 3252 Round Numbers)