题意:
给定一个n*m的棋盘,每天等概率选择一个空的位置放上棋子,当棋盘的每一行和每一列都存在棋子时,下棋结束。问下棋结束时棋盘上棋子数的期望。
思路:
定义:
表示使用k个棋子,覆盖了
行
列的概率。
考虑当前下一个新的棋子,对于棋盘情况的改变有四种情况:
1. 覆盖的行数+1,而列数不变。
2. 覆盖的列数+1,而行数不变。
3. 覆盖的行数列数均+1
4. 覆盖的行数列数均不变。
按这四种情况乘上其相应的概率,转移状态即可。
内存方面可以使用滚动数组进行优化,但优化时需要注意的一个细节是:
而
此题得解。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int A = 50 + 5;
double dp[A][A][2];
int n,m;
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int Mx = n*m;
memset(dp,0,sizeof(dp));
dp[0][0][0] = 1.0;
double ans = 0;
for(int k=1 ;k<=Mx ;k++){
for(int i=1 ;i<=k && i<=n ;i++){
for(int j=1 ;j<=k && j<=m ;j++){
dp[i][j][k&1] = dp[i-1][j][(k&1)^1]*(n-i+1)*j/(1.0*n*m-k+1) +
dp[i][j-1][(k&1)^1]*(m-j+1)*i/(1.0*n*m-k+1) +
dp[i-1][j-1][(k&1)^1]*(n-i+1)*(m-j+1)/(1.0*n*m-k+1);
if(!(i==n && j==m))
dp[i][j][k&1] += dp[i][j][(k&1)^1]*(i*j-k+1)/(1.0*n*m-k+1);
}
}
ans += k*dp[n][m][k&1];
dp[0][0][0] = 0;
}
printf("%.10f\n",ans);
}
return 0;
}