zoukankan      html  css  js  c++  java
  • poj 3252 Round Numbers 【推导·排列组合】

     以sample为例子
     [2,12]区间的RoundNumbers(简称RN)个数:Rn[2,12]=Rn[0,12]-Rn[0,1]
     即:Rn[start,finish]=Rn[0,finish]-Rn[0,start-1]
     所以关键是给定一个X,求出Rn[0,X]
     现在假设X=10100100 
     这个X的二进制总共是8位,任何一个小于8位的二进制都小于X
     第一部分,求出长度为[0,7]区间内的二进制是RoundNumber的个数
      对于一个长度为Len的二进制(最高位为1),如何求出他的RoundNumbers呢(假设为用R(len)来表达),分为奇数和偶数两种情况
      1、奇数情况:在Len=2k+1的情况下,最高位为1,剩下2k位,至少需要k+1为0
       用C(m,n)表示排列组合数:从m个位置选出n个位置的方法
       R(len)=C(2k,k+1)+C(2k,k+2)+...+C(2k,2k).
       由于 A:C(2k,0)+C(2k,1)+...+C(2k,2k)=2^(2k)
         B:C(2k,0)=C(2k,2k), C(2k,1)=C(2k,2k-1) ,,C(2k,i)=C(2k,2k-i)
       于是  C(2k,0)+C(2k,1)+...+C(2k,2k)
        = C(2k,0)+C(2k,1)+...+C(2k,k)+C(2k,k+1)+C(2k,K+2)+...+C(2k,2k)
        = 2*R(len)+C(2k,k)
        =2^(2k)
        所以R(len)=1/2*{2^(2k)-C(2k,k)};
      2. 偶数情况 len=2*k,类似可以推到 R(len)=1/2*(2^(2k-1));
     第二部分,对于上面这个长度为8的例子:即X=10100100,首先如果本身是RoundNumbers,第二部分的结果总数+1
      第一部分已经将长度小于8的部分求出。现在要求长度=8的RoundNumber数目
      长度为8,所以第一个1不可改变
      现在到第二个1,如果Y是前缀如100*****的二进制,这个前缀下,后面取0和1必然小于X,已经有2个0,一个1,剩下的5个数字中至少需要2个0,
       所以把第二个1改为0:可以有C(5,2)+C(5,3)+C(5,4)+C(5,5)
      现在第三个1,也就是前最为101000**,同样求出,至少需要0个0就可,所以有C(2,0)+C(2,1)+C(2,2)个RoundNumbers
      。。。
      将所有除了第一个1以外的1全部变为0,如上算出有多少个RoundNumbers,结果相加(由于前缀不一样,所以后面不管怎么组合都是唯一的)

     将第一部分和第二部分的结果相加,就是最后的结果了。
     精度要求方面,用int就可以了:two billion=20亿<2*1024*1024*1024=2^31,需用31位来表示数组,由于第一位总是1,所以求组合数的时候最多求30,C(30,k),k取值区间是[0,30],因为C(k,i)<2^k,所以结果用int表示就可以


    Problem: 3252  User: ycdoit
    Memory: 148K  Time: 0MS
    Language: C++  Result: Accepted
    #include<iostream>
    using namespace std;
    const int MS=31;
    int C[MS][MS];        //[0,...30]
    int power2[MS];        //1 2 4 8 ... 2^(MS-1)
    int Binary[MS];
    int Solve(int X){
        if(X<=1)    return 0;
        int i,j,k,n0,n1,Len,res=0;
        for(i=0;i<MS;++i)    Binary[i]=((power2[i]&X)!=0)?1:0;
        for(i=MS-1;i>=0 && Binary[i]==0;--i);    //停止的时候,i指向1        //总长度为i+1
        for(Len=i;Len>=1;--Len){        //求出 [1...i]的R(len)
            if(Len%2==1)    res+=(( power2[Len-1]-C[Len-1][(Len-1)/2])>>1);
            else    res+=(power2[Len-1]>>1);
        }
        for(j=i,n0=0,n1=0;j>=0;--j)    if(Binary[j])    ++n1;    else ++n0;
        if(n1<=n0)    ++res;
        for(j=i-1,n0=0,n1=1;j>=0;--j)    {
            if(Binary[j]){    //后面还有j位    第j位临时当做0
                for(k=j;k>=0 && k+n0+1>=j-k+n1;--k)        res+=C[j][k];
                ++n1;
            }
            else ++n0;
        }
        return res;
    }
    int main(){
        int i,j,Start,Finish;
        for(i=0;i<MS;++i)    C[i][0]=1,C[i][i]=1,power2[i]=(1<<i);
        for(i=2;i<MS;++i)    for(j=1;j<i;++j)    C[i][j]=C[i-1][j-1]+C[i-1][j];
        scanf("%d%d",&Start,&Finish);
        printf("%d ",Solve(Finish)-Solve(Start-1));
        return 0;
    }

  • 相关阅读:
    Ext.FormPanel-----FieldSet的用法
    DAO层,Service层,Controller层、View层
    PageProxy分页的实现
    Layout布局(补充)
    Ext--Layout(布局)
    DirectEvents用法
    Linq的使用
    字符串注入攻击
    winform(C#)里弹出“确定”“取消”对话框
    C#的数组
  • 原文地址:https://www.cnblogs.com/767355675hutaishi/p/3811737.html
Copyright © 2011-2022 走看看