/*
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;
}
poj3252(排列组合计数,数位计数)
最新推荐文章于 2019-08-26 16:35:00 发布