考试的时候以为就是简单的概率期望题,考完后知道是简单的概率期望DP题,完美爆零。

  这道题数据范围很小,很容易让人想到状压,不过貌似没什么可压的。那么只能说明这道题复杂度很高了,状态数组f[o][i][j][k]为第o次攻击后出现有i个1滴血的j个2滴血的,k个3滴血的情况的概率。那么转移方程就明了了。

  1.f[o][i][j][k]+=f[o-1][i][j][k]/(i+j+k+1)英雄收到攻击

  2.f[o][i][j][k]+=f[o-1][i+1][j][k]*(i+1)/(i+j+k+2)有一个1滴血的奴隶主被击杀。前提是i+j+k+1<=7

  3.f[o][i][j][k]+=f[o-1][i-1][j+1][k-1]*(j+1)/(i+j+k)有一个2滴血的奴隶主收到攻击,召唤一个奴隶主,前提是i!=0&&k!=0

  4.f[[o][i][j][k]+=f[o-1][i][j-1][k]*(k/i+j+k)有一个3滴血的奴隶主收到攻击,召唤一个奴隶主,前提是j!=0。

  5由于仆从满7个后不会再召唤,所以当i+j+k=7时需要特判受到攻击却未召唤的情况,即f[o][i][j][k]+=f[o-1][i-1][j+1][k],f[o][i][j][k]+=f[o-1][i][j-1][k+1],前提分别为i!=0,j!=0。

  转移方程写出来别挂精度上就行了,但是要注意我们的状态为受到攻击后场上局面的状态,真正结果为0~k-1中所有状态转移方程中1/仆从+1,一开始写的为1~k身败名裂……

 #include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;
int t,n;
double f[][][][];
int main(){
// freopen("defcthun.in","r",stdin);
// freopen("defcthun.out","w",stdout);
scanf("%d",&t);
while(t--)
{
memset(f,,sizeof(f));
scanf("%d",&n);
int xx,yy,zz;
scanf("%d%d%d",&xx,&yy,&zz);
f[][xx][yy][zz]=1.0;
for(int o=;o<n;o++)
{
for(int i=;i<=;i++)
{
for(int j=;j<=;j++)
{
for(int k=;k<=;k++)
{
if(i+j+k>)break;
if(i+j+k+<=) f[o][i][j][k]+=f[o-][i+][j][k]*(double(i+)/double(i++j+k+));
f[o][i][j][k]+=f[o-][i][j][k]/double(i+j+k+);
if(i&&k) f[o][i][j][k]+=f[o-][i-][j+][k-]*(double(j+)/double(i+k+j));
if(j) f[o][i][j][k]+=f[o-][i][j-][k]*(double(k)/double(i+j+k));
if(i+j+k==)
{
if(i) f[o][i][j][k]+=f[o-][i-][j+][k]*double(j+)/double(i+j+k+);
if(j) f[o][i][j][k]+=f[o-][i][j-][k+]*double(k+)/double(i+j+k+);
}
}
}
}
}
double sum=0.0;
for(int o=;o<n;o++)
{
for(int i=;i<=;i++)
{
for(int j=;j<=;j++)
{
for(int k=;k<=;k++)
{
if(i+j+k>)break; sum+=f[o][i][j][k]*1.0/double(j+k+i+);
}
}
}
}
printf("%.2lf\n",sum);
}
//while(1);
return ;
}
04-17 15:35