POJ 1189
教训很多也收获不少的一道题
首先是DP状态的定义,开始一直不知道怎么下手写,后来觉得这个定义比较合适\(dp(i, j)\)定义为当前假设第i行全是没拔掉钉子,落在第i行第j个钉子上的钉子的数量(概率分数处理会很麻烦)
然后是数学上的两个问题:
- 一是概率,想当然的计算从顶向下方法数,结果测试数据一直发现不对劲,就是概率的理解出现了偏差,check别人的代码恍然大悟后利用模拟的思路,假设顶上有\(1<<n\)落下来这样模拟。反思计算方法数从而求得概率,应该是相关的条件概率在其中出现了问题,导致计算方法数是错误的想法
- 二是gcd,简单复习了下
此外,代码中最后循环求和实际测试比直接复制(1<<n)快就比较玄学...
中间WA了三次,主要问题是两处,数据范围没有仔细斟酌,应该使用long long,另一处则是对于0的特殊处理
#include <iostream>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <stack>
#include <map>
#include <set>
using namespace std;
typedef unsigned long long LL;
const int maxn= 55;
char bd[maxn][maxn];
LL dv[maxn][maxn];
inline LL GCD(LL q, LL r)
{
return 0== r ? q : GCD(r, q%r);
}
int main(int argc, char const *argv[])
{
int n, m;
while (~scanf("%d %d", &n, &m)){
for (int i= 1; i<= n; ++i){
for (int j= 1; j<= i; ++j){
scanf(" %c", bd[i]+j);
}
}
dv[1][1]= (((LL)1)<<n);
++n;
for (int i= 2; i<= n; ++i){
for (int j= 1; j<= i; ++j){
dv[i][j]= 0;
if (j!= i && '*'== bd[i-1][j]){
dv[i][j]+= (dv[i-1][j])>>1;
}
if (j> 1){
if ('*'== bd[i-1][j-1]){
dv[i][j]+= (dv[i-1][j-1])>>1;
}
if ('.'== bd[i-2][j-1]){
dv[i][j]+= dv[i-2][j-1];
}
}
}
}
LL sum= 0, ans= dv[n][m+1], gcd;
for (int i= 1; i<= n; ++i){
sum+= dv[n][i];
}
gcd= GCD(sum, ans);
sum/= gcd;
ans/= gcd;
if (ans){
printf("%llu/%llu\n", ans, sum);
}
else{
printf("0/1\n");
}
}
return 0;
}