博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

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);
	}
}
posted @ 2022-02-13 23:08  Allorkiya  阅读(21)  评论(0编辑  收藏  举报