1. 程式人生 > >ZOJ 3822 Domination 概率DP 2014年ACM_ICPC亞洲區域賽牡丹江現場賽D題

ZOJ 3822 Domination 概率DP 2014年ACM_ICPC亞洲區域賽牡丹江現場賽D題

題目大意:

就是現在有一個N*M的棋盤(1 <= N, M <= 50)每次在上面的空位置隨機選一個放一個棋子, 問使得所有行和所有列上都有棋子放置, 放置的棋子的數目期望

大致思路:

這題同步賽的時候沒感覺, 現在做發現就是一個水題, 當時還是太弱了...

狀態轉移方程見程式碼註釋吧..

程式碼如下:

Result  :  Accepted     Memory  :  51276 KB     Time  :  1750 ms

/*
 * Author: Gatevin
 * Created Time:  2014/12/23 20:26:43
 * File Name: Sora_Kasugano.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

/*
 * 用dp[i][j][k]表示對於N*M的棋盤, 已經用k個子佔了i行和j列時到達目標狀態需要的chess piece數量期望
 * 那麼顯然dp[N][M][min(N, M)~N*M] = 0, 狀態轉移方程:
 * dp[i][j][k] =  (N - i)*(M - j)/(N*M - k)*(dp[i + 1][j + 1][k + 1] + 1)
 *              + (N - i)*j/(N*M - k)*(dp[i + 1][j][k + 1] + 1)
 *              + i*(M - j)/(N*M - k)*(dp[i][j + 1][k + 1] + 1)
 *              + (i*j - k)/(N*M - k)*(dp[i][j][k + 1] + 1)
 * 考慮一下邊界條件即可, dp[0][0][0]即為答案
 */

/*
 * 牡丹江那場區域賽在同步賽場外做的時候沒思路, 現在一看的確是個水題..當時還是太弱了
 */

double dp[51][51][2510];

int main()
{
    int t;
    scanf("%d", &t);
    int N, M;
    while(t--)
    {
        scanf("%d %d", &N, &M);
        memset(dp, 0, sizeof(dp));
        for(int i = N; i >= 0; i--)
            for(int j = M; j >= 0; j--)
            {
                if(i == N && j == M) continue;
                for(int k = i*j; k >= min(i, j); k--)
                {
                    dp[i][j][k] = 0;
                    if(i + 1 <= N && j + 1 <= M && (i + 1)*(j + 1) >= k + 1)
                        dp[i][j][k] += (N -i)*(M - j)*1.0/(N*M - k)*(dp[i + 1][j + 1][k + 1] + 1);
                    if(i + 1 <= N && (i + 1)*j >= k + 1)
                        dp[i][j][k] += (N - i)*j*1.0/(N*M - k)*(dp[i + 1][j][k + 1] + 1);
                    if(j + 1 <= M && (j + 1)*i >= k + 1)
                        dp[i][j][k] += i*(M - j)*1.0/(N*M - k)*(dp[i][j + 1][k + 1] + 1);
                    if(i*j >= k + 1)
                        dp[i][j][k] += (i*j - k)*1.0/(N*M - k)*(dp[i][j][k + 1] + 1);
                }
            }
        printf("%.8f\n", dp[0][0][0]);
    }
    return 0;
}