ZOJ 3822 | 2014区域赛牡丹江站 概率DP

题目链接


题意:
给定一个n*m的棋盘,每天等概率选择一个空的位置放上棋子,当棋盘的每一行和每一列都存在棋子时,下棋结束。问下棋结束时棋盘上棋子数的期望。


思路:
定义: d p [ i ] [ j ] [ k ] 表示使用k个棋子,覆盖了 i j 列的概率。

考虑当前下一个新的棋子,对于棋盘情况的改变有四种情况:
1. 覆盖的行数+1,而列数不变。
2. 覆盖的列数+1,而行数不变。
3. 覆盖的行数列数均+1
4. 覆盖的行数列数均不变。

按这四种情况乘上其相应的概率,转移状态即可。

内存方面可以使用滚动数组进行优化,但优化时需要注意的一个细节是:
d p [ 0 ] [ 0 ] [ 0 ] = 1 d p [ 0 ] [ 0 ] [ x ] = 0 ( x > 0 )

此题得解。


代码:

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

猜你喜欢

转载自blog.csdn.net/wubaizhe/article/details/80537366