poj 3252 解题报告

题意:求在区间[start, end]内的Round Number数目,Round Number定义为二进制表示中0的数目大于等于1的数目的整数

         这里start和end的值都在1到2000000000之间。

分析:可以看成求[0, end]内的RoundNumber数 再减去[0, start-1]内的RoundNumber数,具体的求法:对于每一个数A,可以将[0, A]按如下

        方式进行分解:分解成位数小于A的所有整数和位数等于A,且小于等于A的所有数。

        对位数小于A的所有整数,进一步分解为位数为0,1,2,3,...,A的位数-1的所有整数,位数为0的数集合为空集,位数为1的集合包含0和1,位数为2

        的集合包含10和11,位数为3的包含[100,111]这个区间...依次类推,可以预先求出0到31位的所有位数的RoundNumber数,因为2000000000小于

        2^31.这里用到组合数,比如求[10000, 11111]区间内的RoundNumber,就是求组合数C(4, 3) + C(4, 4):后面四位中取得0数目要多于1的数目。

   

       对位数等于A且小于等于A的所有整数,比如[10000,11010],按如下方法进行分析:首先分析A本身:11010是否是RoundNumber,然后,再将这个区间进行划分:

       [10000, 11000), [11000, 11010), [11010, 11010),就是按1的出现从左向右进行划分, 注意到最后一个区间可能没有值,也可能有一个值,这取决于最后一位

      数字是0还是1,然后同样的道理,[11000, 11010)就是[11000, 11001],也是计算组合数。

      对于组合数的计算,我们可以发现前面过程中用到的组合数不超过C(31, 31),所以我们可以提前计算出组合数,使用组合数公式:

      C(a, b) = C(a-1, b) + C(a-1, b-1); C(x, 0) = 1, 且若b > a C(a, b) = 0求出组合数表。

     代码如下:

#include<iostream>
using namespace std;


	int numbers[40];
    int combNumbers[40][40];




    int Comb(int kinds, int choose) {

		return combNumbers[kinds][choose];

	}

	int numOfRNsWithBitsLe(int n){
		int comb = 0;
		for(int i = 0; i <= n; i++)
			comb += numbers[i];
		return comb;
	}

	int conduct(int number) {
	    int length = 0;
	    int list[35];
	    while(number != 0){
			list[length] = number % 2;
			number = number / 2;
			length++;
		}

		int totalSize = length;
		int endCount = numOfRNsWithBitsLe(length - 1);
		int oneCount = 0;
		int zeroCount = 0;
		for(int i = 0; i < length; i++){
			int cur = list[i];
			if(cur == 1)oneCount++;
			else zeroCount++;
		}
		if(zeroCount >= oneCount)endCount += 1;
		if(list[0] == 1){
		    if(zeroCount + 1 >= oneCount - 1)
                endCount++;
		}

		oneCount = 1; // reuse, different meaning
		for(int index = length - 2; index >= 1; index--){
			int cur = list[index];
			if(cur == 1){
				//TODO
				int tmp = 2 * index + 2 * oneCount - totalSize;
				if(tmp <= 0){
				    for(int choose = 0; choose <= index; choose++)
                        endCount += Comb(index, choose);
				}
				else{
                    int choose = (tmp % 2 == 0)?tmp / 2:(tmp / 2 + 1);
                    for(; choose <= index; choose++)
                        endCount += Comb(index, choose);
				}
				oneCount++;
			}
		}

		return endCount;
	}





    void init(){
		for(int i = 0; i < 40; i++)
			for(int j = 0; j <= i; j++){
			    if(!j || i == j)
                    combNumbers[i][j] = 1;
                else
                    combNumbers[i][j] = combNumbers[i-1][j] + combNumbers[i-1][j-1];
			}

		numbers[0] = 0;
		numbers[1] = 1;
		for(int n = 2; n < 40; n++){
			int oneCount = 1;
			int zeroCount = 0;
			int bitCount = n - 1;
			int choose = (n % 2 == 0)?(n / 2):(n / 2 + 1);
			// C(n-1, choose);
			int comb = 0;
			for(;choose <= bitCount; choose++)
				comb += Comb(bitCount, choose);
			numbers[n] = comb;
		}
	}





int main(){
		init();



        int start,end;

        cin>>start>>end;
        int nStart = start - 1;
        int result1 = conduct(nStart);
        int result2 = conduct(end);

        int ret = result2 - result1;
        cout<< ret <<endl;




}






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值