传送门
解析:
比较简单的概率DP。
思路:
我们用
f
i
,
j
f_{i,j}
fi,j表示落到
(
i
,
j
)
(i,j)
(i,j)这个格子上的概率。
那么落到最后第
m
m
m个格子的概率就是
f
n
+
1
,
m
+
1
f_{n+1,m+1}
fn+1,m+1,在最后一行还要下面的一行新建一排格子,表示球的终点。
那么当前位置有钉子时,落向 ( i + 1 , j ) (i+1,j) (i+1,j)和 ( i + 1 , j + 1 ) (i+1,j+1) (i+1,j+1)的概率分别都是落到 ( i , j ) (i,j) (i,j)概率的一半。这时候有状态转移 f [ i + 1 ] [ j ] + = f [ i ] [ j ] / 2 f[i+1][j]+=f[i][j]/2 f[i+1][j]+=f[i][j]/2 f [ i + 1 ] [ j + 1 ] + = f [ i ] [ j ] / 2 f[i+1][j+1]+=f[i][j]/2 f[i+1][j+1]+=f[i][j]/2
而当前位置没有钉子的时候,可以画个图,实际上该位置钉子会直接落到 ( i + 2 , j + 1 ) (i+2,j+1) (i+2,j+1),这时候的状态转移就是 f [ i + 2 ] [ j + 1 ] + = f [ i ] [ j ] f[i+2][j+1]+=f[i][j] f[i+2][j+1]+=f[i][j]
细节:
想必最令人头疼的就是最后的既约分数。
仔细看状态转移,实际上只有不变和除以二两种方式。
再仔细一想,如果没有钉子被拔掉,总的下落方案数就是 2 n 2^n 2n。
那就好办了,直接把 f [ 1 ] [ 1 ] f[1][1] f[1][1]设置成 2 n 2^n 2n就能保证转移中只有整数。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline
char getop(){
re char c;
while('.'!=(c=gc())&&'*'!=c);
return c;
}
int n,m;
bool ex[51][51];
ll f[53][53];
signed main(){
cin>>n>>m;
for(int re i=1;i<=n;++i)
for(int re j=1;j<=i;++j)
ex[i][j]=getop()=='.';
f[1][1]=1ll<<n;
for(int re i=1;i<=n;++i){
for(int re j=1;j<=i;++j){
if(ex[i][j]){
f[i+2][j+1]+=f[i][j];
}
else {
f[i+1][j]+=f[i][j]>>1;
f[i+1][j+1]+=f[i][j]>>1;
}
}
}
ll ans=f[n+1][m+1];
ll b=1ll<<n;
while(0==(ans&1)&&0==(b&1))ans>>=1,b>>=1;
cout<<ans<<"/"<<b<<endl;
return 0;
}