本文分析部分参考了http://blog.csdn.net/zhengnanlee/article/details/9794625
第一道组合数学题,连跪一天。。
没有discuss根本做不出来,但是想想不是很难。
首先,
RoundNumber[start, end] = RoundNumber[0,start] - RoundNumber[0,end - 1] = RoundNumber[0, start + 1] - RoundNumber[0, end]
这是容易证明的。因为一个数字是否是所谓“RoundNumber”,只跟它自己有关,也就是说转化成二进制的0的个数是否大于1的个数有关。那么他们(独立的数字,如1,2,3,4)之间是相互独立的。至于为何要加一或者减一,因为是要包含区间边界,无论是否小于等于它都会包含(省事儿),所以最后处理一下。
《组合数学》第二章“排列与组合”中,阐述了几个原理:加法原理、减法原理、乘法原理。各自的前提是不同的,需要仔细去了解。
重点不在上面,上面只是基础。
首先在高中我们学过组合数的定义:
然后它具有以下常用性质:
1.
2.
3.
4.
性质3常用于打表,因为其具递推的性质。
切入正题,假设有如下二进制数:
问其是否是RoundNumber?答案是肯定的。
最一开始我们知道,要想求RN[start,end],我们只需要求RN[0,start+1]-RN[0,end]。归根结底是求RN[0,x]。
把这个八位二进制数分情况讨论一下,比它小的分两种情况,一是1至7位数,二是八位数。
情况一:
一个二进制,长度为Len的数,若使0的个数大于1的个数,可以这么安排:
若Len是奇数,即Len=2*k+1,最高位是1,其余2k位,则有:
至此,Len是奇数时我们解决了。可以直接算这些组合数(从表中直接取,以下代码就是这么操作的),也可以利用一下上面提供的性质。由性质2知,在性质1和式函数中,最头的两个是对称相等的。那么,上式恰好等于:
若Len是偶数,Len=2*k,最高位是1,其余2k-1位,同理可得:
情况二:当位数为8时:
先放大一下这个二进制数:
可以清晰地看到,第四位,我标记了1。如果将1改为0,那么剩下的四位任意填数都满足该数(新的)小于这个二进制数(10011000)B。但是不能任意填,还是要满足0的个数大于1的个数,那么我们有如下结论:
令l为长度,i为当前改变的“1”的位置,如图中的位置4,zero为0的计数(绝对计数、不包含被改变的第四位数1),a为需要填数的位置(下图中的虚线框)中1的个数,b为需要填数的位置中0的个数。
有如下等式:
有如下不等式:
解得:
这个式子说明了,至少要填那么b个零,也就是l/2-(zero+1)。注意奇偶。至多呢?至多全填满呗。填多少?i-1个(空格数)呗。注意,程序中是从高位到地位搜索,i的位置表示的是绿色的1的位置,i-1是空格(虚线框)的个数。
至此,问题解决了,那么代码就很简单了。
#include<iostream>
using namespace std;
int c[33][33] = { 0 };
int bin[35]; //十进制n的二进制数
void combinations() {
for (int i = 0; i <= 32; i++) {
for (int j = 0; j <= i; j++) {
if (!j || i == j) {
c[i][j] = 1;
} else {
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
}
}
}
return;
}
void dec_to_bin(int n) {
bin[0] = 0;
while (n) {
bin[++bin[0]] = n % 2;
n /= 2;
}
return;
}
int round(int n) {
int i, j;
int sum = 0;
dec_to_bin(n);
/*计算长度小于bin[0]的所有二进制数中RN的个数*/
for (i = 1; i < bin[0] - 1; i++) {
for (j = i / 2 + 1; j <= i; j++) {
sum += c[i][j];
}
}
/*计算长度等于bin[0]的所有二进制数中RN的个数*/
int zero = 0; //从高位向低位搜索过程中出现0的位的个数
for (i = bin[0] - 1; i >= 1; i--) {
if (bin[i]) {
for (j = (bin[0] + 1) / 2 - (zero + 1); j <= i - 1; j++) {
sum += c[i - 1][j];
}
} else
zero++;
}
return sum;
}
int main(){
combinations();
int a,b;
while(scanf("%d%d",&a,&b)!=EOF){
printf("%d\n",round(b+1)-round(a));
}
return 0;
}