dp zoj3822 Domination

来源:互联网 发布:movielens数据集下载 编辑:程序博客网 时间:2024/06/08 18:17

传送门:点击打开链接

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


0 0
原创粉丝点击