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);
	}
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号