题解:概率与期望DP
这道题需要石头、剪刀、布分开推。
一推石头为例 ,初始时f[i][0][0]=1,设tot=C(i+j+k,2)
f[i][j][k]=f[i][j-1][k]*i*j/tot+f[i][j][k-1]*j*k/tot+f[i-1][j][k]*k*i/tot+f[i][j][k]*(C(i,2)+C(j,2)+C(k,2))
移项推出f[i][j][k]。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 103
using namespace std;
int n,m,k;
double f[N][N][N];
double calc(double x)
{
return x*(x-1)/2;
}
int main()
{
// freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++) f[i][0][0]=1;
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
for (int t=0;t<=k;t++) {
if(i+j+t<=t) continue;
if (j==0&&t==0) continue;
double tot=calc(i+j+t);
if (j)f[i][j][t]+=i*j*1.0/tot*f[i][j-1][t];
if (i)f[i][j][t]+=i*t*1.0/tot*f[i-1][j][t];
if (t)f[i][j][t]+=j*t*1.0/tot*f[i][j][t-1];
double tmp=1.0;
tmp-=calc(i)/tot; tmp-=calc(j)/tot; tmp-=calc(t)/tot;
f[i][j][t]/=tmp;
}
printf("%.12lf ",f[n][m][k]);
memset(f,0,sizeof(f));
for (int i=1;i<=m;i++) f[0][i][0]=1;
for (int j=1;j<=m;j++)
for (int i=0;i<=n;i++)
for (int t=0;t<=k;t++) {
if(i+j+t<=t) continue;
if (i==0&&t==0) continue;
double tot=calc(i+j+t);
if (j)f[i][j][t]+=i*j*1.0/tot*f[i][j-1][t];
if (i)f[i][j][t]+=i*t*1.0/tot*f[i-1][j][t];
if (t)f[i][j][t]+=j*t*1.0/tot*f[i][j][t-1];
double tmp=1.0;
tmp-=calc(i)/tot; tmp-=calc(j)/tot; tmp-=calc(t)/tot;
f[i][j][t]/=tmp;
}
printf("%.12lf ",f[n][m][k]);
memset(f,0,sizeof(f));
for (int i=1;i<=k;i++) f[0][0][i]=1;
for (int t=1;t<=k;t++)
for (int i=0;i<=n;i++)
for (int j=0;j<=m;j++) {
if(i+j+t<=t) continue;
if (i==0&&j==0) continue;
double tot=calc(i+j+t);
if (j)f[i][j][t]+=i*j*1.0/tot*f[i][j-1][t];
if (i)f[i][j][t]+=i*t*1.0/tot*f[i-1][j][t];
if (t)f[i][j][t]+=j*t*1.0/tot*f[i][j][t-1];
double tmp=1.0;
tmp-=calc(i)/tot; tmp-=calc(j)/tot; tmp-=calc(t)/tot;
f[i][j][t]/=tmp;
}
printf("%.12lf\n",f[n][m][k]);
}