题目链接:http://poj.org/problem?id=1189
解题思路:
此题有几个要注意的地方:
1 因为钉子最多有50层,2<<50超出了int的表示范围,所以要用long long类型(64位整数)。
2 如果某个位置钉子不在了,则该位置的概率垂直下落到下下层对应位置,即(i,j)处的概率直接赋予位置(I+2, j+1)。
3 当某个位置(i,j)的钉子在,则将该位置的概率除以1/2再传递到(i+1,j)和(i+1, j+1)两个位置。在传递概率时要注意(i+1,j)和(i+1, j+1)两处可能已经有概率(传递前概率不为0)。
注意以上几个问题后,解题思路就明了了
首先位置(0,0)的概率为1,然后令i从0到n-1,j从0到i来遍历所有位置,对每个位置(i,j)按照钉子在不在两种情况分别传递概率。最后输出底层第m个位置的概率,输出时要将概率化为既约分数(0写成0/1)。
#include<iostream>
using namespace std;
long long a[51][51]; //表示概率的分子
long long b[51][51]; //表示概率的分母
//将(i,j) 处的概率的1/2传递到(i+1,t)
void transmit(int i, int j, int t)
{
long long temp = b[i][j]<<1;
if(a[i+1][t])
{
if(b[i+1][t] != temp)
{
if(b[i+1][t] < temp)
{
while(b[i+1][t] < temp)
{
a[i+1][t] <<= 1;
b[i+1][t] <<= 1;
}
a[i+1][t] += a[i][j];
}
else
{
long long temp_a = a[i][j];
while(temp < b[i+1][j])
{
temp <<= 1;
temp_a <<= 1;
}
a[i+1][t] += temp_a;
}
}
else
{
a[i+1][t] += a[i][j];
}
}
else
{
a[i+1][t]=a[i][j];
b[i+1][t]=temp;
}
}
long long gcd(long long a, long long b)
{
long long temp;
while(b != 0)
{
temp = a;
a = b;
b = temp % b;
}
return a;
}
int main()
{
int n,m;
char ch;
//输入数据
cin>>n>>m;
//计算概率
a[0][0]=1;
b[0][0]=1;
for(int i=0; i<n; i++)
for(int j=0; j<=i; j++)
{
cin>>ch;
if(!a[i][j]) continue;
if(ch=='*') // 钉子在
{
//更新a[i+1][j] b[i+1][j]
transmit(i, j, j);
//更新a[i+1][j+1] b[i+1][j+1]
transmit(i, j, j+1);
}
else //钉子不在
{
a[i+2][j+1]=a[i][j];
b[i+2][j+1]=b[i][j];
}
}
long long temp_a = a[n][m];
long long temp_b = b[n][m];
if(!temp_a) cout<<"0/1"<<endl;
else
{
long g = gcd(temp_a, temp_b);
while(g != 1)
{
temp_a /= g;
temp_b /= g;
g = gcd(temp_a, temp_b);
}
cout<<temp_a<<"/"<<temp_b<<endl;
}
return 0;
}