教訓很多也收穫不少的一道題
首先是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;
}