题目链接:https://nanti.jisuanke.com/t/40512
题意:n个硬币,初始全是反面朝上,抛m次,每次抛k个,求最好情况硬币向上个数的期望
一个比较好的概率DP的总结:https://blog.csdn.net/myjs999/article/details/81022546
分析:很明显当硬币反面朝上次数大于k,直接抛这些,小于k时,这些反面朝上全部取的,不够的加正面朝上的,dp[i][j]表示抛i次后正面朝上有j个的概率,枚举每次抛的硬币里面正面朝上有t个,状态转移方程就是dp[i+1]]j+t]=dp[i][j]*(1/2)^k*Ctk(t个里面选k个正面朝上的)
#include <bits/stdc++.h> using namespace std; const int maxn=110; const long long mod=1e9+7; const int inf=1<<30; typedef long long ll; double dp[210][210]; //dp[i][j]即为抛了i次后,有j枚硬币正面朝上的概率 //这里开大点,后面就可以不用判断第二维加上k之后会超100的问题 double p[110];//p数组存放的是1/2的幂次 double c[110][110]; //c数组是求组合数的,别的方法都涉及到取模 int main(){ c[0][0]=1; for(int i=1;i<=100;i++) { c[i][0]=1; for(int j=1;j<=i;j++)c[i][j]=c[i-1][j-1]+c[i-1][j];//这个方法求组合数的方法是用到了杨辉三角,高中学的二项式定理 } p[0]=1; for(int i=1;i<=100;i++)p[i]=p[i-1]/2; int t;cin>>t; for(int T=1;T<=t;T++){ memset(dp,0,sizeof(dp)); int n,m,t;scanf("%d%d%d",&n,&m,&t); dp[0][0]=1; for(int i=0;i<m;i++){ for(int j=0;j<=n;j++){ if(dp[i][j]==0)continue;//小优化,如果dp[i][j]是0的话,转移来的也都是0,直接不用考虑 for(int k=0;k<=t;k++){ //反面朝上的硬币数目不大于每次要抛的数目的话,这次就全抛反面朝上的 if(n-j>=t)dp[i+1][j+k]+=dp[i][j]*p[t]*c[t][k]; else dp[i+1][j+k-(t-(n-j))]+=dp[i][j]*p[t]*c[t][k]; //注意两个都是+= } } } double ans=0; for(int i=1;i<=n;i++){ ans+=dp[m][i]*i; //cout<<dp[m][i]<<endl; } printf("%.3lf ",ans); } return 0; }