dp zoj3822 Domination

传送门:点击打开链接

题意:一个棋盘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;
}


你可能感兴趣的:(dp zoj3822 Domination)