[CSP-S模拟测试]:石头剪刀布(rps)(概率DP)
题目传送门(内部题9)
输入格式
第一行一个整数$n$。接下来$n$行每行$3$个非负整数$r_i,p_i,s_i$。
输出格式
一行一个实数表示答案。当你的答案与标准答案的绝对或相对误差不超过${10}^{-9}$时判为正确。
样例
样例输入:
3
300 0 0
0 300 0
0 0 300
样例输出:
6.333333333333
数据范围与提示
对于$10%$的数据,$n=1$。
对于$30%$的数据,$n\leqslant 10$。
对于另外$10%$的数据,所有$r_i$均相等,所有$p_i$均相等。
对于又另外$30%$的数据,$r_i=0$。
对于$100%$的数据,$1\leqslant n\leqslant 50$,$r_i+p_i+s_i=300$。
题解
首先,这道题是一道假期望。
用$g[i][j][k]$表示第$i+j+k$轮出了$i$个石头,$j$个剪刀,$k$个布的概率。
用$dp[i][j][k][l]$表示前$i+j+k$轮出了$i$个石头,$j$个剪刀,$k$个布的概率。
注意我们不是在$dp[i][j][k][x]$中取$\max$,而是在$dp[i][j][k][x]+dp[i][j][k][x+1]\times 3$中取$\max$。
$g[i][j][k]$也非常好求,直接给出式子:$g[i][j][k]=g[i-1][j][k]\times r[t]+g[i][j-1][k]\times s[t]+g[i][j][k-1]\times p[t]$。
注意$dp$和$g$的意义,所以对$g$的转移需要在$dp$之后。
还有就是注意输入顺序是:石头、布、剪刀,即可。
时间复杂度:$\Theta(n^4)$。
期望的分:$100$分。
实际的分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
int n;
long double w[100],z[100],c[100];
long double C[51][51];
long double g[51][51][51],dp1[51][51][51],dp2[51][51][51],dp3[51][51][51];
long double ans;
int main()
{
scanf("%d",&n);
for(int i=0;i<=n;i++)
{
C[i][0]=1.0;
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
for(int i=1;i<=n;i++)
{
cin>>w[i]>>c[i]>>z[i];//注意输入顺序
w[i]/=300.0;
z[i]/=300.0;
c[i]/=300.0;
}
g[0][0][0]=1.0;
for(int t=1;t<=n;t++)
for(int i=t;~i;i--)
for(int j=t-i;~j;j--)
for(int k=t-i-j;~k;k--)
{
if(i+j+k!=t)
{
if(i)dp1[i][j][k]+=dp1[i-1][j][k]*w[t];
if(j)dp1[i][j][k]+=dp1[i][j-1][k]*z[t];
if(k)dp1[i][j][k]+=dp1[i][j][k-1]*c[t];
dp1[i][j][k]+=g[i][j][k]*w[t];
if(i)dp2[i][j][k]+=dp2[i-1][j][k]*w[t];
if(j)dp2[i][j][k]+=dp2[i][j-1][k]*z[t];
if(k)dp2[i][j][k]+=dp2[i][j][k-1]*c[t];
dp2[i][j][k]+=g[i][j][k]*z[t];
if(i)dp3[i][j][k]+=dp3[i-1][j][k]*w[t];
if(j)dp3[i][j][k]+=dp3[i][j-1][k]*z[t];
if(k)dp3[i][j][k]+=dp3[i][j][k-1]*c[t];
dp3[i][j][k]+=g[i][j][k]*c[t];
}
if(i)g[i][j][k]+=g[i-1][j][k]*w[t];
if(j)g[i][j][k]+=g[i][j-1][k]*z[t];
if(k)g[i][j][k]+=g[i][j][k-1]*c[t];
}
for(int i=0;i<n;i++)
for(int j=0;i+j<n;j++)
for(int k=0;i+j+k<n;k++)
ans+=max(dp1[i][j][k]+3.0*dp2[i][j][k],max(dp2[i][j][k]+3.0*dp3[i][j][k],dp3[i][j][k]+3.0*dp1[i][j][k]))/(C[n][i+j+k]*(n-i-j-k));
cout<<fixed<<setprecision(12)<<ans<<endl;
return 0;
}
rp++