2016集训测试赛(二十四)Problem C: 棋盘控制

Description

Solution

场上的想法(显然是错的)是这样的: 我们假设棋子是一个一个地放置的, 考虑在放置棋子的过程中可能出现哪些状态. 我们令有序整数对((i, j))表示总共控制了(i)(j)列的情况, 我naive地认为一个状态要么不出现, 要么只出现一次. 于是用(f[i][j])来表示出现的概率, 直接进行DP. 然后我用随机函数对拍, 发现是WA的...

考虑问题出现在了哪里: 一个状态实际上是可以出现多次的. 比如说我们考虑分别控制了两行两列的状态: 两行两列产生4个交点, 这4个交点中可以有2个, 3个, 4个棋子. 因此我们发现还要多记录一维, 表示用了多少个棋子.

我们用(f[x][i][j])表示用了(x)个棋子, 控制了(i)(j)列的状态的出现概率.

[egin{aligned} f[x][i][j] = &f[x - 1][i - 1][j - 1] imes frac{mn - (i - 1)m - (j - 1)n + (i - 1)(j - 1)}{mn - (x - 1)} \ &+ f[x - 1][i - 1][j] imes frac{j imes (n - (i - 1))}{mn - (x - 1)} \ &+ f[x - 1][i][j - 1] imes frac{i imes (m - (j - 1))}{mn - (x - 1)} \ &+ f[x - 1][i][j] imes frac{i imes j - (x - 1)}{mn - (x - 1)} end{aligned} ]

考虑如何统计答

[ans = sum_x f[x][i][j] imes x ]

同时我们注意到这个式子还不完全是对的: (f[x - 1][n][m])不能用于继续转移.

因此我们在(f[x][n][m])上直接减去(f[x - 1][n][m])即可.

#include <cstdio>
#include <cstring>

const int N = 50, M = 50;
double f[N * M + 1][N + 1][M + 1];
int main()
{
    int cs; scanf("%d", &cs);
    while(cs --)
    {
        int n, m; scanf("%d%d", &n, &m);
        memset(f, 0, sizeof(f));
        f[0][0][0] = 1;
        for(int x = 1; x <= n * m; ++ x)
        {
            for(int i = 1; i <= n; ++ i)
                for(int j = 1; j <= m; ++ j)
                    f[x][i][j] = f[x - 1][i - 1][j - 1] * (m * n - (i - 1) * m - (j - 1) * n + (i - 1) * (j - 1)) / (m * n - (x - 1))
                                 + f[x - 1][i - 1][j] * (j * (n - (i - 1))) / (m * n - (x - 1))
                                 + f[x - 1][i][j - 1] * (i * (m - (j - 1))) / (n * m - (x - 1))
                                 + f[x - 1][i][j] * (i * j - (x - 1)) / (m * n - (x - 1));
            f[x][n][m] -= f[x - 1][n][m];
        }
        double ans = 0;
        for(int i = 1; i <= n * m; ++ i) ans += i * f[i][n][m];
        printf("%.10lf
", ans);
    }
}

原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7515953.html