1. 程式人生 > 其它 >ZOJ 3288 概率dp

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