poj3252

第一道组合数学题,连跪一天。。

没有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 (cin >> a >> b)
	{
		cout << round(b + 1) - round(a) << endl;
	}
}

组合数学,一跪一天,爽!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值