poj3252(排列组合计数,数位计数)

/*
translation:
	给出两个数,求这两个数形成的闭区间内round number的个数。一个数转换成二进制的时候其中0的个数比1多的
	或者相等的称为round number。
solution:
	排列组合。
	要求闭区间内的round number个数,只需要求出0~s的个数和0~t的个数,将二者相减即可得到答案。
	关于个数的计算,可以采用如下方法。当一个数为n位的二进制时,只需要令这n位二进制至少有(n+1)/2
	个0,即可满足条件。利用排列组合的递推公式可以轻松做到。再利用加法原理求出来答案。
note:
	1:利用上述方法可以求出来0~n-1位所有二进制数的round number个数,但是还有一些n位的二进制没有计算。
	而根据给的数据范围来看,这些遗漏的数的个数可能达到10^9个之多。本来想对这些“漏网之鱼”用暴力计算,但
	这样一来肯定超时。想来想去没有想到好办法。后来看题解采用了下面这种方法:
	首先明白,要计算的数肯定都比所给定的数小,但是二进制位数都一样。所以对二进制位从高到低扫描,遇到0的话用一个变量
	计数,遇到1的话,将当前位当成0,这样后来的无论怎么组合都是小于原数的,所以计算的时候要多减去一个1.同时同样利用排列组合
	计数。
date:
	2016.10.14
*/
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
const int maxn = 35;

int s, t;
int c[maxn][maxn], bit[maxn];	//bit[i]表示i位二进制位在最高位是1的情况下能够表示最少的数字
int cnt[maxn];
int bin[maxn];

void init() {
	//对组合数组进行打表
	memset(c, 0, sizeof(c));
	for(int i = 0; i <= maxn; i++) {
		c[i][i] = c[i][0] = 1;
		for(int j = 1; j < i; j++) {
			c[i][j] = c[i-1][j] + c[i-1][j-1];
		}
	}

	memset(cnt, 0, sizeof(cnt));
	for(int i = 1; i <= maxn; i++) {
		int t = (i+1) / 2;
		for(int j = t; j <= i-1; j++) {
			cnt[i] += c[i-1][j];	//求出在i位二进制数下,满足round number的组合数
		}
	}

}

void getBit(int x) {
	memset(bin, 0, sizeof(bin));
	while(x) {
		bin[++bin[0]] = x % 2;
		x /= 2;
	}
}

int compute(int x) {	//左闭右开,计算[0,x)的round number个数
	getBit(x);	//获得二进制下的位数存储在bin[0]中,并且转换为二进制

	int res = 0;
	for(int i = 1; i < bin[0]; i++) {
		res += cnt[i];	//求出1~bin[0]-1位二进制中round number的个数。
	}

	int zeroNum = 0;
	for(int i = bin[0]-1; i >= 1; i--) {	//计算遗漏的数字
		if(bin[i] == 1) {
			for(int j = (bin[0]+1) / 2 - (zeroNum + 1); j < i; j++)	//从高到低,+1是因为把当前位看成0,这样任意组合的数就比原数小
				res += c[i-1][j];
		}
		else	zeroNum++;
	}
	return res;
}

int main()
{
	init();
	while(cin >> s >> t) {
		cout << compute(t+1) - compute(s) << endl;
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值