ZOJ 3288 概率dp
题目大意
给定一个\(n \times m\)的棋盘,每天随机往里面一个空位置放一个棋子,问期望多少天之后棋盘每一行都有至少一个棋子,每一列至少一个棋子。
\(n,m \leq 50\)
思路
设\(f_{i,j,k}\)表示用\(k\)个棋子覆盖\(i\)行\(j\)列的概率是多少。
每次转移只有四种方式:
- 选已经覆盖但是仍有空缺的位置进行放置,注意已经处理到\(n,m\)的时候没有此项
- 选一个行未覆盖过列覆盖过的位置
- 选一个行覆盖过列未覆盖过的位置
- 选一个行列都为覆盖过的位置
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 60;
double f[N][N][N*N];
int main () {
int T;
cin >> T;
while(T --) {
int n,m;
cin >> n >> m;
memset(f,0.0,sizeof f);
f[0][0][0] = 1;
for(int i = 1;i <= n; i ++) {
for(int j = 1;j <= m; j ++) {
for(int k = 1;k <= n * m;k ++) {
double p1 = f[i][j][k - 1] * (i * j - (k - 1));
double p2 = f[i - 1][j][k - 1] * (n - i + 1) * j;
double p3 = f[i][j - 1][k - 1] * i * (m - j + 1);
double p4 = f[i - 1][j - 1][k - 1] * (n - i + 1) * (m - j + 1);
if(i == n && j == m) {
f[i][j][k] = (p2 + p3 + p4) / (n * m - (k - 1));
}else {
f[i][j][k] = (p1 + p2 + p3 + p4) / (n * m - (k - 1));
}
}
}
}
double ans = 0;
for(int i = 1;i <= n * m; i ++ ) {
ans += f[n][m][i] * i;
}
printf("%.10lf\n",ans);
}
}