【组合数学】“派送食物问题”

| OI, CSDN, 组合数学, 概率

问题描述

Lucy 和Lily 是一对双胞胎,在Lucy 和Lily 十岁生日的时候,她们举办了一个晚会。晚会上,Tom 负责派送食物。Tom 手上有两种食物,汉堡和三明治。晚会上的20 个孩子围成一圈,Tom 拿了10 个汉堡和10 个三明治,进行分发。为了保证公平,Tom 采用丢硬币的方法,正面给汉堡,背面给三明治。Lucy 和Lily 被按排到最后再拿食物,但当Tom 发到第19 个人Lucy 的时候,这时候只有两个汉堡了,于是他不再丢硬币了,让最后两个人都拿汉堡。Lucy 和Lily 因此都拿了汉堡。对此,Tom 很好奇,他非常想知道当n(n 为偶数)个人围成一圈的时候,分发n/2 个汉堡,n/2 个三明治,Lucy 和Lily 最后两个拿食物,她们拿到同样食物的概率。

数据输入

第一行整数k,测试数据的组数,接下来k 行,每行一个偶整数n。其中,n 的取值范围是[2,4,6…100000]。

数据输出

对于每组测试数据,输出Lucy 和Lily 拿到同样食物的概率。输出四舍五入保留3 位有效数字。

样例输入

3 
6
10
256

样例输出

0.625
0.727
0.950

数据说明

30%的测试数据n 的取值范围小于50。 70%的测试数据n 的取值范围小于等于1000。 100%的测试数据n 的取值范围小于等于100000。


这完全就是一道数学题啊。。。由于我十分愚钝,丝毫不会。。。 我们先把题解发出来

所以最后两个数不同的概率是\(\mathrm{C}_{n-2}^{n/2-1}\cdot(\frac 1 2)^{n-2}\) 。因此,最后两个数相同的概率就是 \(1-\mathrm{C}_{n-2}^{n/2-1}\cdot(\frac 1 2)^{n-2}\) 。由于n实在太大了,在计算的时候要注意乘法和除法的结合,既要避免浮点数溢出,又要避免精度降到0之下。


上来那个“所以”弄得我莫名其妙,不知道这式子怎么蹦出来的(当然神犇们一看题一眼就会了)。下面给出我愚蠢的过程。


我先敲了如下的模拟(数学课本上说,不好算的概率,可以用计算机模拟)

var
	a,b,tot,i,c,n:longint;
begin
	randomize;
	tot:=10000000;
	c:=0;
	n:=10;
	for tot:=1 to tot do
		begin
			a:=n>>1;b:=n>>1;
			for i:=1 to n-2 do
				if random(2)=1 then dec(a) else dec(b);
			if (a<=0)or(b<=0) then inc(c);
			//if (a=0)or(b=0) then inc(c);
		end;
	writeln(c/tot:0:3);
end.

这个模拟充斥着我迂腐的思想。。。我在注释的那一行纠结了很长时间,后来觉得就是小于等于零,而不是等于零,因为小于零对应着某一种“提前取完”。(其实把样例输进去看下就知道了)。


通过这个模拟我们发现符合要求的情况就是n-2个数,每个可以取0或1,取得大于等于n/2个0或1。计算过程打草


这样就和题解上的一样了,就剩下“由于n实在太大”了,在计算的时候要注意乘法和除法的结合,既要避免浮点数溢出,又要避免精度降到0之下的处理,我又用了愚蠢的方法,把组合数定义里的所有分子分母存起来,先乘法,太大之后做除法。


var
	t,n:longint;
	x,y:array[0..300000]of longint;

function calc(n:longint):extended;
	var i,j,a,b:longint;ans:extended;
	begin
		b:=n-2;
		for i:=1 to b do y[i]:=2;
		a:=n>>1-1;
		for i:=1 to a do
			begin
				x[i]:=n-1-i;
				inc(b);
				y[b]:=i;
			end;
		ans:=1;
		i:=1;
		j:=1;
		//for i:=1 to b do write(y[i],' ');
		while i<=a do
			begin
				ans:=ans*x[i];
				while (ans>1e15)and(j<=b) do begin ans:=ans/y[j];inc(j);end;
				inc(i);
			end;
		while j<=b do
			begin
				ans:=ans/y[j];
				inc(j);
			end;
		exit(1-ans);
	end;

begin
	assign(input,'food.in');reset(input);
	assign(output,'food.out');rewrite(output);
	readln(t);
	for t:=1 to t do
		begin
			read(n);
			writeln(calc(n):0:3);
		end;
	close(input);close(output);
end.

原文地址