Gym 101174 D - Dinner Bet —— 概率DP

This way

题意:

盒子里有n个球,一开始每个人拿出c个,记录他们的编号。
接下来每次操作:拿出d个,如果这d个中有和一开始的c个相同的,那么就标记这个球,如果某个人的c个数都被标记过了,游戏结束,问你游戏进行的次数的期望

题解:

两个数混在一起的话不好搞,如果每次都要判断的话会很麻烦,不如分成3种数:a独有的,b独有的,共有的。
它这个两个人其中的任意一个拿完了就结束了,那么顺着推的话感觉就像是一个原点发散出去的感觉,但是如果dp[i][j][k]表示a独有的拿了i个,b独有的拿了j个,共有的拿了k个的话,,不知道步数最终怎么算呢,不清楚,那么反一下,可以逆着:dp[i][j][k]表示剩下i,j,k,没拿,然后枚举每种拿了多少转移,或者反向转移(也就是从拿了往没拿转移),下面分别是两种情况,就是转移的时候有一些变化而已。
这个是正向的:没拿往拿了转移
loop表示自环的概率

sum+=com[c-i][n1]*com[c-j][n2]*com[sam-k][n3]/com[n][d]*com[n-(c-i)-(c-j)-(sam-k)][d-n1-n2-n3]*dp[i+n1][j+n2][k+n3];

c-i表示a独有的拿了c-i个,其余类似
com[n-(c-i)-(c-j)-(sam-k)][d-n1-n2-n3]表示拿了不在范围的概率

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
const ll N=55;
ld com[N][N],dp[15][15][15];
//i,j,k,not be signed
bool vis[N];
int main(){
    for(int i=0;i<N;i++)
        com[i][0]=com[i][i]=1;
    for(int i=1;i<N;i++)
        for(int j=1;j<i;j++)
            com[i][j]=com[i-1][j]+com[i-1][j-1];
    int n,d,c,sam=0;
    cin>>n>>d>>c;
    int x;
    int cc=c;
    for(int i=1;i<=cc;i++){
        cin>>x;vis[x]=1;
    }
    for(int i=1;i<=cc;i++){
        cin>>x;c-=vis[x],sam+=vis[x];
    }
    for(int i=c;~i;i--){
        for(int j=c;~j;j--){
            for(int k=sam;~k;k--){
                if(i+k==cc||j+k==cc)continue;
                ld loop=com[n-(c-i)-(c-j)-(sam-k)][d]/com[n][d];
                ld sum=1;
                for(int n1=0;n1<=c-i;n1++){
                    for(int n2=0;n2+n1<=d&&n2<=c-j;n2++){
                        for(int n3=0;n3+n2+n1<=d&&n3<=sam-k;n3++){
                            if(!n1&&!n2&&!n3)continue;
                            sum+=com[c-i][n1]*com[c-j][n2]*com[sam-k][n3]/com[n][d]*com[n-(c-i)-(c-j)-(sam-k)][d-n1-n2-n3]*dp[i+n1][j+n2][k+n3];
                        }
                    }
                }
                //if(!i&&!j&&!k)loop=0;
                dp[i][j][k]=sum/(1-loop);
            }
        }
    }
    cout<<fixed<<setprecision(5)<<dp[0][0][0]<<endl;
    return 0;
}

或者是拿了往没拿方向转移,也就是逆着转移:

for(int i=0;i<=c;i++){
        for(int j=0;j<=c;j++){
            for(int k=0;k<=sam;k++){
                if(i+k==0||j+k==0)continue;
                ld loop=com[n-i-j-k][d]/com[n][d];
                ld sum=1;
                for(int n1=0;n1<=i;n1++){
                    for(int n2=0;n2+n1<=d&&n2<=j;n2++){
                        for(int n3=0;n3+n2+n1<=d&&n3<=k;n3++){
                            if(!n1&&!n2&&!n3)continue;
                            sum+=com[i][n1]*com[j][n2]*com[k][n3]/com[n][d]*com[n-i-j-k][d-n1-n2-n3]*dp[i-n1][j-n2][k-n3];
                        }
                    }
                }
                dp[i][j][k]=sum/(1-loop);
            }
        }
    }
   	cout<<fixed<<setprecision(5)<<dp[c][c][sam]<<endl;
发布了584 篇原创文章 · 获赞 33 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/105257889