zoj 3822 概率dp

Edward 喜欢下棋,每天下班他会把一个棋子放到棋盘中空闲的位置,有一天他发现棋盘 was dominated by the chess pieces,意思就是每一行,每一列都至少有一个棋子在上面。Edward 觉得很有趣,他想知道在一个N*M的棋盘中摆成这样的情况需要的天数的期望是多少。
14年牡丹江现场赛的题目。现在看来也不算是特别难了吧?

题目主要卡手的地方我觉得在于棋子只能放在空闲的地方。首先可以想到两个状态,一个是已经放好的行数,一个是已经放好的列数。但对于这个只能放在空闲的位置的情况,这两个状态明显解决不了。于是昨天晚上在床上前想了一下,居然想通了。添加一维状态表示当前棋盘上已经放置的棋子数量。

dp[i][j][k] 表示已经在 i 行 j 列都放有棋子并且棋子总数是 k 的情况下的期望。

对于下一个要放置的棋子,有四种可能,分别是
1):当前行,当前列都有棋子。 p1 = (i*j-s)/(n*m-k)
2):当前行有棋子,当前列没有。p2 = (i*(m-j))/(n*m-k)
3):当前行没有棋子,当前列有。p3 = ((n-i)*j)/(n*m-k)
4):当前行,当前列都没有棋子。p4 = ((n-i)*(m-j))/(n*m-k)

得到转移方程:
dp[i][j][k] = p1*dp[i][j][k+1] + p2*dp[i][j+1][k+1] + p3*dp[i+1][j][k+1] + p4*dp[]i+1[j+1][k+1] + 1;

// bz一开始写的时候概率推错了,而且初始化还少写一行,心塞至极。

/***********************************************
 ** problem ID  : zoj_3822.cpp
 ** create time : Sat Aug 01 10:01:01 2015
 ** auther name : xuelanghu
 ** auther blog : blog.csdn.net/xuelanghu407
 **********************************************/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int N, M;
double dp[55][55][2550];

void init() {
    for (int i=0; i<55; i++) {
        for (int j=0; j<55; j++) {
            for (int k=0; k<2550; k++) {
                dp[i][j][k] = -1.0;
            }
        }
    }
}

double DP (int a, int b, int s) {
    if (a > N || b > M || s > a * b) return dp[a][b][s] = 0.0;
    if (dp[a][b][s] != -1.0) return dp[a][b][s];
    if (a == N && b == M) return dp[a][b][s] = 0.0;

    double p1 = (a * b - s) * 1.0 / (N * M - s);
    double p2 = ((N-a) * b) * 1.0 / (N * M - s);
    double p3 = (a * (M-b)) * 1.0 / (N * M - s);
    double p4 = ((N-a) * (M-b)) * 1.0 / (N * M - s);

    double res = 1.0;

    res += DP(a, b, s + 1) * p1;
    res += DP(a + 1, b, s + 1) * p2;
    res += DP(a, b + 1, s + 1) * p3;
    res += DP(a + 1, b + 1, s + 1) * p4;

    return dp[a][b][s] = res;
}

int main () {// freopen("in.txt", "r", stdin);
    int T;
    scanf ("%d", &T);
    for (; T--; ) {
        scanf("%d%d", &N, &M);
        init();
        double ans = DP(0, 0, 0);
        printf ("%.10lf\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值