传送门:点击打开链接
题意:一个棋盘N*M大,每次在空的位置放棋子,求使得每一行至少有1个棋子,每一列至少有1个棋子的期望值。一旦满足条件,就停止摆棋
思路:通过概率dp,然后再去求期望,然而这个dp的状态很难设。
最容易一开始会想到状态压缩。。其实没必要把状态记录的这么详细。
设dp[i][j][k]为k个棋子摆放,恰好覆盖了i行,j列
现在我们考虑摆放一个棋子,那么这个棋子可能会存在4种状态。
摆放后,覆盖的行和列的数量不变
摆放后,覆盖的行数+1,覆盖的列数不变
摆放后,覆盖的行数不变,覆盖的列数+1
摆放后,覆盖的行和列均加1
对这4种状态方程都写出来,撸一发
但是统计答案的时候需要注意一些细节。对于dp[n][m][k]来说,可能是通过dp[n][m][k-1]转移过来的,然而这样是不允许的。
所以统计答案时候的概率应该是dp[n][m][k]-dp[n][m][k-1]
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #define fuck printf("fuck") #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MX = 50 + 5; double dp[MX][MX][MX*MX]; int main() { int n, m, T; //FIN; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); memset(dp, 0, sizeof(dp)); int sum = n * m; dp[0][0][0] = 1; for(int k = 1; k <= sum; k++) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { if(i > k || j > k) continue; dp[i][j][k] += dp[i][j][k - 1] * (i * j - k + 1) / (sum - k + 1); dp[i][j][k] += dp[i - 1][j][k - 1] * ((n - i + 1) * j) / (sum - k + 1); dp[i][j][k] += dp[i][j - 1][k - 1] * (i * (m - j + 1)) / (sum - k + 1); dp[i][j][k] += dp[i - 1][j - 1][k - 1] * ((n - i + 1) * (m - j + 1)) / (sum - k + 1); } } } double ans = 0; for(int i = max(n, m); i <= sum; i++) { ans += (dp[n][m][i] - dp[n][m][i - 1]) * i; } printf("%.12lf\n", ans); } return 0; }