这道题是一道原创的数学题,我的思路是直接算出询问点处的概率概率,仅供大家参考。
题目链接:http://acm.ustc.edu.cn/ustcoj/problem.php?id=1388
经过分析:不难得出,钻石下落后的行为是堆成一个小三角形,然后往两边继续堆,堆至最高点又形成一个稍大的三角形,然后继续往两边堆,如此往复.
因此,我们得出结论,对于n颗钻石堆成的一个小三角形加他的两个边,这样的钻石结构,如果询问点在小三角形内,概率p = 1;如果询问点在两边上,则需要特殊对待,
利用数学方法求得概率,而不符合以上两种的点,概率p = 0;
关键难点在于询问点在两边上的位置时的概率计算.(一开始曾考虑递归计算,后来发现写出来非常麻烦,wa了以后无法调试,就放弃了);
Step1:
我们先考虑两种简单情况,最后来解决数学问题.
显然如果钻石数是1, 6, 15, 28.....则恰好构成三角形,没有多余,判断点是否在三角形内也十分简单,
我们假设由这n颗钻石堆成的三角形底边是len,表示有len颗钻石,而不是表示长度,(len为奇数,1, 3, 5, 7...),虽然有可能出现如下图的情况:
但我还是把他当做底边为1的三角形和他的两条边来处理,而不是底边为2, 总之把对称轴放在钻石下落的线上好处理
那么底边为len的三角形,,最高处的钻石的高度坐标为len - 1,判断是否在三角形内只需要判断表达式:abs(x) + y < len - 1成立即可;
显然两边上的点,x,y满足abs(x) + y == len - 1,内部小于.
所以我们一起解决了,在三角形内,三角形外的两种情况.
Step2:
接下来算点在两边上的概率;
这时由于n的大小改变,我们易想到,如果n除了用于堆中间三角形的钻石,剩下的钻石还比较多的话,把一条边堆满了,剩下钻石就只会往另一边堆,
这是不是等概率的,而在那之前都是等概率,所以我们把这两种情况分开计算,
先判断是否能把一条边堆满(堆满是指:底边长有len的三角形,边上有len+1颗钻石,例如上图中,左边的边就被堆满了,这以后只能往右边落)
一共有n颗钻石,堆三角形用了(len + 1) * len / 2颗,剩下的即为两者之差d,差d和len + 1比较大小,大则必然会有钻石不能等概率选择左右落法,
小则之前的这么多颗钻石d都是等概率左右分配.
如果至多堆满一边,这d颗钻石每次等概率左右分配, 所有情况有2 ^ d种,对于询问的点,纵坐标反映了它所在边至少要有的钻石数,如果纵坐标是y,
那么至少要y + 1颗钻石, 所以符合要求的情况有C(y + 1, d)+ C(y + 2, d)+....+C(d, d).C(n, m)为组合数, 表示从m中选n个, 这种情况就是从d颗下落钻石中分别选y+1,y+2......d颗落在一边上.
如果满足可以把一条边堆满的情况,我们其实可以把他化为前一种情况,因为d > len + 1了,我们求出d和len+ 1的差记为t,那么我们知道无论怎么下落,两边上都至少有t颗钻石,(抽屉原理思想),又由于钻石是没有区别的,我们不妨把两边都先放上t颗钻石,那么可以知道,剩余的d-2t颗钻石最多填满一条边,化为了前一种情况,只需要注意一下,这时落在某边上的钻石数,从至少y+1颗变成了至少,y+1 - t颗,注意这个值可能是负的,说明,询问的点被我们提前处理时已经放上了钻石,所以特判,p = 1,其他就按原来的组合数求解即可.
最后可以写出如下代码,仅供参考:
#include <stdio.h>
#include <math.h>
#include<stdlib.h>
int com(int n, int m)
{
long long re = 1, i;
for (i = 1; i <= n; ++i)
re *= (m + 1 - i);
for (i--; i > 1; i--)
re /= i;
return re;
}
double pp(int x, int y, int n)
{
int len, tem, i;
for (len = 1; (len * len + len) / 2 < n; len += 2);
if (abs(x) + y > len - 1)
return 0.0;
else
{
if (x == 0)
{
if (y)
{
if (n >= (y + 1) * (y + 2) / 2)
return 1.0;
else
return 0.0;
}
else
return 1.0;
}
else
{
if (abs(x) + y < len - 1)
return 1.0;
else
{
long long sum = 0;
tem = n - (len - 2)* (len - 1) / 2;
if (tem <= len - 1)
{
for (i = y + 1; i <= tem; ++i)
sum += com(i, tem);
return (double) sum / (double) (1 << tem);
}
else
{
int t = tem - len + 1;
if (t > y + 1)
return 1.0;
else
{
tem -= 2 * t;
for (i = y - t + 1; i <= tem; ++i)
sum += com(i, tem);
return (double) sum / (double) (1 << tem);
}
}
}
}
}
}
int main()
{
int n, x, y, t, tt = 1;
scanf("%d", &t);
while (t--)
{
scanf("%d%d%d", &n, &x, &y);
printf("Case #%d: %.6lf\n", tt++, pp(x, y, n));
}
return 0;
}
过程中写了一个组合数的函数,计算组合数可能会超过数据类型限制,所以题目中给的n不大.再大一点难度就会增加了,需要另外方法求出组合数,或者换个思路求概率。总之,这题就是这样啦。