【概率DP】Gym - 101174 - D - Dinner Bet

题目链接http://codeforces.com/gym/101174

题意

N N N 个球标号为 1 1 1 N N N,两个人分别写下 C C C 个数字,每次随机选出 D D D 个球,若干次后,如果某个人写的 C C C 个数字都出现过,则游戏结束。问游戏持续回合数的期望。


题解

概率DP。

d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 的状态表示两个人的公共数字有 k k k 个还没被抽到,第一个人有 i i i 个数字没被抽到,第二个人剩 j j j 个。 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 的值表示这种状态下持续回合数的期望。

d p [ 0 ] [ j ] [ 0 ] dp[0][j][0] dp[0][j][0] d p [ i ] [ 0 ] [ 0 ] dp[i][0][0] dp[i][0][0] 的答案都为 0 0 0,因为此时游戏已经结束了。

假设某一次抽球,抽到 x x x 个第一个人的数字, y y y 个第二个人的, z z z 个共有的,抽到这种情况的概率我们假设是 p p p
p = C i x × C j y × C k z × C n − i − j − k d − x − y − z C n d p=\frac{C_i^x\times C_j^y\times C_k^z\times C_{n-i-j-k}^{d-x-y-z}}{C_n^d} p=CndCix×Cjy×Ckz×Cnijkdxyz
假设抽球并没有改变状态,概率是 q q q
q = C d n − i − j − k C n d q=\frac{C_d^{n-i-j-k}}{C_n^d} q=CndCdnijk
那么转移方程就可以写成:
d p [ i ] [ j ] [ k ] = ( d p [ i − x ] [ j − y ] [ k − z ] + 1 ) p + ( d p [ i − x ] [ j − y ] [ k − z ] + 2 ) p q + ⋯ + ( d p [ i − x ] [ j − y ] [ k − z ] + n ) p q n − 1 dp[i][j][k]=(dp[i-x][j-y][k-z]+1)p+(dp[i-x][j-y][k-z]+2)pq+\cdots+(dp[i-x][j-y][k-z]+n)pq^{n-1} dp[i][j][k]=(dp[ix][jy][kz]+1)p+(dp[ix][jy][kz]+2)pq++(dp[ix][jy][kz]+n)pqn1
为了方便起见,我们把 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 记为 S S S d p [ i − x ] [ j − y ] [ k − z ] dp[i-x][j-y][k-z] dp[ix][jy][kz] 记为 d p dp dp ,然后利用小学知识化简一下
S = ( d p + 1 ) p + ( d p + 2 ) p q + ⋯ + ( d p + n ) p q n − 1 q S = ( d p + 1 ) p q + ( d p + 2 ) p q 2 + ⋯ + ( d p + n ) p q n \begin{aligned} S&=(dp+1)p+(dp+2)pq+\cdots+(dp+n)pq^{n-1}\\ qS&=(dp+1)pq+(dp+2)pq^2+\cdots+(dp+n)pq^{n} \end{aligned} SqS=(dp+1)p+(dp+2)pq++(dp+n)pqn1=(dp+1)pq+(dp+2)pq2++(dp+n)pqn
上式减下式得
( 1 − q ) S = ( d p + 1 ) p + p q + p q 2 + ⋯ + p q n − 1 − ( d p + n ) p q n S = ( d p + 1 ) p + p q ( 1 − q n − 1 ) 1 − q − ( d p + n ) p q n 1 − q \begin{aligned} (1-q)S&=(dp+1)p+pq+pq^2+\cdots+pq^{n-1}-(dp+n)pq^n\\ S&=\frac{(dp+1)p+p\frac{q(1-q^{n-1})}{1-q}-(dp+n)pq^n}{1-q}\\ \end{aligned} (1q)SS=(dp+1)p+pq+pq2++pqn1(dp+n)pqn=1q(dp+1)p+p1qq(1qn1)(dp+n)pqn
然后根据大学知识可得 q n = 0 q^n=0 qn=0,最后化简成:
S = ( d p + 1 ) p 1 − q + p q ( 1 − q ) 2 S=\frac{(dp+1)p}{1-q}+\frac{pq}{(1-q)^2} S=1q(dp+1)p+(1q)2pq
所以,只要写六层转移就行了。


#include 
using namespace std;
typedef long long ll;
typedef long double db;
const int N=1e6+7;
db dp[60][60][60];
int n,d,c;
int a[60],b[60];
int na,nb,nc,nd;
int vis[60];
db fac[60];
db C(int n,int m){
    if(n<m) return 0;
    return fac[n]/fac[m]/fac[n-m];
}
int main()
{
    fac[0]=fac[1]=1;
    for(int i=2;i<=50;i++) fac[i]=fac[i-1]*i;
    scanf("%d%d%d",&n,&d,&c);
    for(int i=1;i<=c;i++) scanf("%d",&a[i]),vis[a[i]]+=1;
    for(int i=1;i<=c;i++) scanf("%d",&b[i]),vis[b[i]]+=2;;
    for(int i=1;i<=n;i++){
        if(vis[i]==1) na++;
        if(vis[i]==2) nb++;
        if(vis[i]==3) nc++;
    }
    ///printf("%d>>%d>>%d\n",na,nb,nc);
    for(int i=0;i<=na;i++){
        for(int j=0;j<=nb;j++){
            for(int k=0;k<=nc;k++){

                if(i==0&&k==0) continue;
                if(j==0&&k==0) continue;

                for(int x=0;x<=i&&x<=d;x++){
                    for(int y=0;y<=j&&x+y<=d;y++){
                        for(int z=0;z<=k&&x+y+z<=d;z++){

                            if(x==0&&y==0&&z==0) continue;


                            int oth=n-i-j-k;
                            db p=C(i,x)*C(j,y)*C(k,z)*C(oth,d-x-y-z)/C(n,d);
                            db q=C(oth,d)/C(n,d);
                            ///printf("p=%f  q=%f\n",(double)p,(double)q);
                            dp[i][j][k]+=((dp[i-x][j-y][k-z]+1)*p+p*q/(1-q))/(1-q);
                        }
                    }
                }
                ///printf("dp[%d][%d][%d]=%f\n",i,j,k,(double)dp[i][j][k]);
            }
        }
    }
    printf("%.10f\n",(double)dp[na][nb][nc]);
}

你可能感兴趣的:(动态规划)