ZOJ 3329 One Person Game(概率dp)

来源:互联网 发布:ubuntu 17.04 iso安装 编辑:程序博客网 时间:2024/05/22 01:57

dp求期望的题。
    题意:
    有三个均匀的骰子,分别有k1,k2,k3个面,初始分数是0,
    当掷三个骰子的点数分别为a,b,c的时候,分数清零,否则分数加上三个骰子的点数和,
    当分数>n的时候结束。求需要掷骰子的次数的期望。
    题解:
    设 E[i]表示现在分数为i,到结束游戏所要掷骰子的次数的期望值。
    显然 E[>n] = 0; E[0]即为所求答案;
    E[i] = ∑Pk*E[i+k] + P0*E[0] + 1; (Pk表示点数和为k的概率,P0表示分数清零的概率)
    由上式发现每个 E[i]都包含 E[0],而 E[0]又是我们要求的,是个定值。
    设 E[i] = a[i]*E[0] + b[i];
    将其带入上面的式子:
    E[i] = ( ∑Pk*a[i+k] + P0 )*E[0] + ∑Pk*b[i+k] + 1;
    显然,
    a[i] = ∑Pk*a[i+k] + P0;
    b[i] = ∑Pk*b[i+k] + 1;
    当 i > n 时:
    E[i] = a[i]*E[0] + b[i] = 0;
    所以 a[i>n] = b[i>n] = 0;
    可依次算出 a[n],b[n]; a[n-1],b[n-1] ... a[0],b[0];
    则 E[0] = b[0]/(1 - a[0]);

#include<iostream>
#include<cstdio>
using namespace std;
const int N=502;
double sa[N],sb[N],p[20];
int n,k1,k2,k3,a,b,c;
void init()
{
 int i,j,k;
 double s=1.0/(k1*k2*k3);
 fill(p,p+20,0);
 for(i=1;i<=k1;i++)
 {
  for(j=1;j<=k2;j++)
  {
   for(k=1;k<=k3;k++)
   {
    if(i==a&&j==b&&k==c) continue;
    p[i+j+k]+=s;
   }
  }
 }
}
int main()
{
 int t,i,j,s;
 cin>>t;
 while(t--)
 {
  scanf("%d%d%d%d",&n,&k1,&k2,&k3);
  scanf("%d%d%d",&a,&b,&c);
  fill(sa,sa+502,0);
  fill(sb,sb+502,0);
  init();
  s=k1+k2+k3;
  double p0=1.0/(k1*k2*k3);
  for(i=n;i>=0;i--)
  {
   for(j=3;j+i<=n&&j<=s;j++)
   {
    sa[i]+=p[j]*sa[i+j];
                sb[i]+=p[j]*sb[i+j];
   }
   sa[i]+=p0;sb[i]+=1;
  }
  printf("%.15lf\n",sb[0]/(1-sa[0]));
 }
 return 0;
}

原创粉丝点击