zoj3822 Domination 2014亚洲区域赛牡丹江站


Description
Edward is the headmaster of Marjar University. He is enthusiastic about chess and often plays chess with his friends. What's more, he bought a large decorative chessboard with Nrows and M columns.
Every day after work, Edward will place a chess piece on a random empty cell. A few days later, he found the chessboard was dominated by the chess pieces. That means there is at least one chess piece in every row. Also, there is at least one chess piece in every column.
"That's interesting!" Edward said. He wants to know the expectation number of days to make an empty chessboard of N × M dominated. Please write a program to help him.
Input
There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:
There are only two integers N and M (1 <= N, M <= 50).
Output
For each test case, output the expectation number of days.
Any solution with a relative or absolute error of at most 10-8 will be accepted.
Sample Input
2
1 3
2 2
Sample Output
3.000000000000
2.666666666667

题意:n*m的棋牌,每天能够放一个棋子,直到每一行每一列都至少有一个棋子为止,求天数的期望

当时在现场也是晕,太弱了,很简单的一道概率dp呀,只需要多开一维来记录棋子用了多少个就好了呀,当时就在为怎么记录下在了哪里而犯愁,我们也不需要知道它具体下在了哪个空格上,只需要知道有多少行和列已经被占据了就好了,╮(╯▽╰)╭,下了场才突然意识到自己有多蠢,不发牢骚了。


/*
法一:
倒推法,这时dp定义的不是概率而是放了i个棋占了j行与k列之后要完全domination所需的天数,那么dp[i][n][m]很显然就是0天
那么直接dp全初始化为0即可,然后dp[i][j][k]要通过dp[i+1][j][k],dp[i+1][j+1][k],dp[i+1][j][k+1],dp[i+1][j+1][k+1]推得,另外要加一。
最后输出dp[0][0][0];
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;

double dp[2505][55][55];
int n,m;
int T;

int main(){
scanf("%d",&T);
while(T--)
{
        scanf("%d%d",&n,&m);
        memset(dp,0,sizeof(dp));
        for(int j=n;j>=0;j--)
            for(int k=m;k>=0;k--)
            {
                if(j==n&&k==m) continue;
                for(int i=k*j;i>=max(k,j);i--)
                {
                    dp[i][j][k]+=1.0;
                    dp[i][j][k]+=1.0*(j*k-i)/(n*m-i)*dp[i+1][j][k];
                    dp[i][j][k]+=1.0*(n-j)*k/(n*m-i)*dp[i+1][j+1][k];
                    dp[i][j][k]+=1.0*j*(m-k)/(n*m-i)*dp[i+1][j][k+1];
                    dp[i][j][k]+=1.0*(n-j)*(m-k)/(n*m-i)*dp[i+1][j+1][k+1];
                }
            }
            printf("%.12lf\n",dp[0][0][0]);
}
return 0;
}


法二:
dp[i][j][k],i代表天数,j为已占据的行数,k为已占据的列数,而dp的意义是下了i个棋占据j行与k列的概率是多少,而不是期望。所以需要正推,就是利用期望的定义来求期望了。
那么概率的状态转移方程如下:
dp[i][j][k]=dp[i-1][j-1][k]*(n-j+1 )*k/(m*n+1-i)+dp[i-1][j][k-1]*(m-k+1)*j/(m*n+1-i)+dp[i-1][j-1][k-1]*(n-j+1)*(m-k+1)/(n*m-1+i)
如果j或k不到n或m,还要加上dp[i][j][k]+=dp[i-1][j][k]*(j*k-i+1)/(n*m-i+1);
初始化只要dp[1][1][1]=1即可,因为下一个棋子占据1行1列是肯定的。然后递推即可。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;

double dp[3000][51][51];//j代表列,k代表行

int main(){
int T;
double n,m;
scanf("%d",&T);
while(T--)
{
     scanf("%lf%lf",&n,&m);
     memset(dp,0,sizeof(dp));
     dp[1][1][1]=1;
     for(int i=1;i<=m*n+1-min(n,m);i++)
     {
         for(int j=1;j<=i&&j<=n;j++)
         {
             for(int k=1;k<=i&&k<=m;k++)
             {
                 if(dp[i][j][k]>0)
                 {
                     dp[i+1][j][k+1]+=dp[i][j][k]*(m-k)*j/(m*n-i);
                     dp[i+1][j+1][k]+=dp[i][j][k]*(n-j)*k/(m*n-i);
                     dp[i+1][j+1][k+1]+=dp[i][j][k]*(n-j)*(m-k)/(m*n-i);
                     if(j<n||k<m)
                     {
                         dp[i+1][j][k]+=dp[i][j][k]*(j*k-i)/(m*n-i);
                     }
                 }
             }
         }
     }
     double ans=0;
     int n1=n,m1=m;
     for(int i=1;i<=m*n-1+min(n,m);i++)
     {
         ans+=dp[i][n1][m1]*i;//期望定义
     }
     printf("%.12lf\n",ans);
}
return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值