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\)列的状态的出现概率.

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

考虑如何统计答

\[ans = \sum_x f[x][i][j] \times 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\n", ans);
    }
}

posted @ 2017-09-13 17:06  Zeonfai  阅读(151)  评论(0编辑  收藏  举报