Edward is the headmaster of Marjar University. He is enthusiastic about chess and often plays chess with his friends. What's more, he bought a large decorative chessboard with N rows and M columns.
Every day after work, Edward will place a chess piece on a random empty cell. A few days later, he found the chessboard was dominated by the chess pieces. That means there is at least one chess piece in every row. Also, there is at least one chess piece in every column.
"That's interesting!" Edward said. He wants to know the expectation number of days to make an empty chessboard of N × M dominated. Please write a program to help him.
Input
There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:
There are only two integers N and M (1 <= N, M <= 50).
Output
For each test case, output the expectation number of days.
Any solution with a relative or absolute error of at most 10-8 will be accepted.
Sample Input
2 1 3 2 2
Sample Output
3.000000000000 2.666666666667
Author: JIANG, Kai
Source: The 2014 ACM-ICPC Asia Mudanjiang Regional Contest
做了这么多概率dp,结果这道还是没做出来,心情已经不能用郁闷二字来形容了。。。
一、直接求期望
首先是状态的问题,一直在用二维,其实在算概率的时候就应该意识到二维的概率好难算,数据又是50的,很明显要用三维啊!真是笨死了!
dp[i][j][k]代表走了k步,已经有i行,j列安放了棋子。
接下来就是算概率的问题
很明显有四种可转移状态:
1、dp[i][j][k+1]表示走完k+1步,仍是有i行,j列安放了棋子。即安放的第k+1个棋子在i,j所占据的区域,概率为 (i * j - k) / (n * m - k);
2、dp[i][j+1][k+1]表示走完k+1步,有i行,j + 1列安放了棋子。即安放的第k+1个棋子在i行中但不在j列,概率为 i * (m - j) / (n * m - k);
3、dp[i+1][j][k+1]表示走完k+1步,有i + 1行,j列安放了棋子。即安放的第k+1个棋子在j列中但不在i行,概率为 (n - i) * j / (n * m - k);
4、dp[i+1][j+1][k+1]表示走完k+1步,有i + 1行,j + 1列安放了棋子。即安放的第k+1个棋子既不在j列中也不在i行,概率为 (n - i) * (m - j) / (n * m - k);
然后是初始化问题
当i == n && j == m时候,dp[i][j][k] = 0;
无意义的状态全部初始为零,没有影响。
#include <cstdio> #include <iostream> #include <sstream> #include <cmath> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <map> #include <set> #include <queue> #include <stack> #include <algorithm> using namespace std; #define ll long long #define _cle(m, a) memset(m, a, sizeof(m)) #define repu(i, a, b) for(int i = a; i < b; i++) #define repd(i, a, b) for(int i = b; i >= a; i--) #define sfi(n) scanf("%d", &n) #define sfl(n) scanf("%lld", &n) #define pfi(n) printf("%d ", n) #define pfl(n) printf("%lld ", n) #define MAXN 55 double dp[MAXN][MAXN][MAXN * MAXN]; int main() { int T; sfi(T); while(T--) { int n, m; sfi(n), sfi(m); _cle(dp, 0); for(int i = n; i >= 0; i--) for(int j = m; j >= 0; j--) for(int k = i * j; k >= max(i, j); k--) { if(n == i && j == m) continue; dp[i][j][k] += (dp[i][j][k + 1] + 1.0) * 1.0 * (i * j - k); dp[i][j][k] += (dp[i][j + 1][k + 1] + 1.0) * 1.0 * (i * (m - j)); dp[i][j][k] += (dp[i + 1][j][k + 1] + 1.0) * 1.0 * (j * (n - i)); dp[i][j][k] += (dp[i + 1][j + 1][k + 1] + 1.0) * 1.0 * ((n - i) * (m - j)); dp[i][j][k] = dp[i][j][k] / (1.0 * (n * m - k)); //printf("%d %d %d : %.12lf ", i, j, k, dp[i][j][k]); } printf("%.12lf ", dp[0][0][0]); } return 0; }
二、先求概率,再求期望
dp[i][j][k]表示放k个棋子达到有i行,j列安放了棋子的概率。
这里有一大误区,就是开始我和我队友都搞错了,结果还以为自己是算的不对,其实是思考错了,就是在算期望是应只考虑真正起作用的棋子,eg:
2 2
dp[2][2][3] = 1, dp[2][2][4] = 1;
其实在放第四颗棋子时就已经必然为两行两列,第四颗棋子已不起作用,放与不放无关痛痒,所以,放第k颗棋子的概率为:
dp[i][j][k] - dp[i][j][k - 1];
#include <cstdio> #include <iostream> #include <sstream> #include <cmath> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <map> #include <set> #include <queue> #include <stack> #include <algorithm> using namespace std; #define ll long long #define _cle(m, a) memset(m, a, sizeof(m)) #define repu(i, a, b) for(int i = a; i < b; i++) #define repd(i, a, b) for(int i = b; i >= a; i--) #define sfi(n) scanf("%d", &n) #define sfl(n) scanf("%lld", &n) #define pfi(n) printf("%d ", n) #define pfl(n) printf("%lld ", n) #define MAXN 55 double dp[MAXN][MAXN][MAXN * MAXN]; int main() { int T; sfi(T); while(T--) { int n, m; sfi(n), sfi(m); _cle(dp, 0); dp[0][0][0] = 1.0; repu(i, 1, n + 1) repu(j, 1, m + 1) repu(k, max(i, j), i * j + 1) { if(k < 1) continue; dp[i][j][k] += dp[i][j][k - 1] * 1.0 * (i * j - k + 1); dp[i][j][k] += dp[i - 1][j][k - 1] * 1.0 * ((n - i + 1) * j); dp[i][j][k] += dp[i][j - 1][k - 1] * 1.0 * (i * (m - j + 1)); dp[i][j][k] += dp[i - 1][j - 1][k - 1] * 1.0 * ((n - i + 1) * (m - j + 1)); dp[i][j][k] /= (1.0 * (n * m - k + 1)); //printf("%d %d %d : %.12lf ", i, j, k, dp[i][j][k]); } double ans = 0.0; repu(i, max(n, m), n * m + 1) ans += (dp[n][m][i] - dp[n][m][i - 1]) * 1.0 * i; printf("%.12lf ", ans); //double f = ans * 3.0; } return 0; }