有一个三角形木板,竖直立放,上面钉着n(n+1)/2颗钉子,还有(n+1)个格子(当n=5时如图1)。每颗钉子和周围的钉子的距离都等于d,每个格子的宽度也都等于d,且除了最左端和最右端的格子外每个格子都正对着最下面一排钉子的间隙。
让一个直径略小于d的小球中心正对着最上面的钉子在板上自由滚落,小球每碰到一个钉子都可能落向左边或右边(概率各1/2),且球的中心还会正对着下一颗将要碰上的钉子。例如图2就是小球一条可能的路径。
我们知道小球落在第i个格子中的概率pi= ,其中i为格子的编号,从左至右依次为0,1,...,n。
现在的问题是计算拔掉某些钉子后,小球落在编号为m的格子中的概率pm。假定最下面一排钉子不会被拔掉。例如图3是某些钉子被拔掉后小球一条可能的路径。
输入:第1行为整数n(2<=n<=50)和m(0<=m<=n)。以下n行依次为木板上从上至下n行钉子的信息,每行中‘*’表示钉子还在,‘.’表示钉子被拔去,
注意在这n行中
空格符可能出现在
任何位置。
输出:仅一行,是一个既约分数(0写成0/1),为小球落在编号为m的格子中的概率pm
分析
设三角形有n行,第i行(1<=i<=n)有i个铁钉位置,其编号为0..i-1;第n+1行有n+1个铁钉位置,排成n+1个格子,编号为0..n。设经过位置(i,j)的小球个数为Pi,j,则落入格子m的小球个数为Pn+1,m,问题要求的是Pn+1,m /2n。
假设位置(i,j)有铁钉,则各有Pi,j /2个小球落入位置(i+1,j)和位置(i+1,j+1);否则Pi,j 个小球将全部落入位置(i+2,j+1)。
可得如下算法:
P1,0 ← 2n ;
for i←1 to n do
for j←1 to n do if 位置(i,j)有钉子 then
{Pi+1,j← Pi+1,j + Pi,j /2 ;
Pi+1,j+1← Pi+1,j+1 + Pi,j /2 ;
} else Pi+2,j+1← Pi+2,j+1 + Pi,j ;
问题求的是既约分数,因为分母为2的次幂,因此可把分子、分母同时约去2的因子,直至分子不能整除2。
很奇怪的是为什么我用_int64没能通过,然后改为long long ,发现还有RE,想到数组太小了,把sum[50][50]改为sum[51][51]。很幸运只要三次就通过了。
#include<iostream>
using namespace std;
char sum[51][51];
long long numod[52][52];
long long funp(int n,int m ) //设经过位置(i,j)的小球个数为Pi,j
{
int i,j;
long long t;
for(i=0,t=1;i<n-1;i++)
t*=2;
if(n==1 && m==0)
return t;
for(i=1;i<=n;i++)
for(j=0,numod[1][0]=t;j<=i;j++)
{
if(sum[i][j]=='*')
{
numod[i+1][j]+=numod[i][j]/2;
numod[i+1][j+1]+=numod[i][j]/2;
}
else if(sum[i][j]=='.')
numod[i+2][j+1]+=numod[i][j];
}
return numod[n][m];
}
int main()
{
int n,m,i,j;
long long t;
scanf("%d%d",&n,&m);
memset(sum,' ',sizeof(sum));
memset(numod,0,sizeof(numod));
for(i=1;i<=n;i++)
{
for(j=0;j<i;j++)
{
cin>>sum[i][j];
}
}
long long sumN=funp(n+1,m);// 求出落入m号格子的球的个数
//求出概率
for(i=0,t=1;i<n;i++)
t*=2;
while(sumN%2==0 && t%2==0)
{
sumN/=2;
t/=2;
}
//输出结果
printf("%lld/%lld\n",sumN,t);
return 0;
}