bzoj 4832: 抵制克苏恩 (概率与期望DP)

题目描述

传送门

题目大意::有1一个英雄和若干随从奴隶主,克苏恩会攻击 K 次,每次会从对方场上的英雄和随从中随机选择一个并对其产生 1 点伤害。现在对方有一名克苏恩,你有一些奴隶主作为随从,每名奴隶主的血量是给定的。
如果克苏恩攻击了你的一名奴隶主,那么这名奴隶主的血量会减少 1 点,当其血量小于等于 0 时会死亡,如果受到攻击后不死亡,并且你的随从数量没有达到 7 ,这名奴隶主会召唤一个拥有 3 点血量的新奴隶主作为你的随从;如果克苏恩攻击了你的英雄,你的英雄会记录受到 1 点伤害。
已知克苏恩的攻击次数,场上分别有 1 点、 2 点、 3 点血量的奴隶主数量,问英雄收到伤害点数的期望。

题解

f[i][a][b][c] 表示打了i次1点血量的奴隶主数量为a,2点血量的奴隶主数量为b,3点血量的奴隶主数量为c的概率。
E[i][a][b][c] 表示打了i次1点血量的奴隶主数量为a,2点血量的奴隶主数量为b,3点血量的奴隶主数量为c的期望点数。
两个一起维护,然后推就可以了。
为什么不能直接维护期望?因为这是顺着推的,我们利用的期望=概率*权值,如果我们改变状态,把状态表示成剩余i次没打,那么就可以直接逆推期望了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
double f[60][10][10][10][10],E[60][10][10][10][10],inf;
int k,A,B,C;
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    int T; scanf("%d",&T);
    while (T--) {
        memset(f,0,sizeof(f)); 
        memset(E,0,sizeof(E));
        scanf("%d%d%d%d",&k,&A,&B,&C);
        f[0][A+B+C][A][B][C]=1.0;
        for (int i=0;i<=k;i++) 
         for (int j=0;j<=7;j++)
          for (int a=0;a<=j;a++)
           for (int b=0;b<=j-a;b++){
            int c=j-a-b;
            if (f[i][j][a][b][c]==0) continue;
            double p=1.0/(1.0+(double)j);
            f[i+1][j][a][b][c]+=f[i][j][a][b][c]*p;
            E[i+1][j][a][b][c]+=(E[i][j][a][b][c]+f[i][j][a][b][c])*p;
            if (a) f[i+1][j-1][a-1][b][c]+=f[i][j][a][b][c]*p*a,
                   E[i+1][j-1][a-1][b][c]+=E[i][j][a][b][c]*p*a;
            if (b) {
                 if (j<7) 
                    f[i+1][j+1][a+1][b-1][c+1]+=f[i][j][a][b][c]*p*b,
                    E[i+1][j+1][a+1][b-1][c+1]+=E[i][j][a][b][c]*p*b;
                 else 
                    f[i+1][j][a+1][b-1][c]+=f[i][j][a][b][c]*p*b,
                    E[i+1][j][a+1][b-1][c]+=E[i][j][a][b][c]*p*b;
               }
            if (c) {
                if (j<7) 
                 f[i+1][j+1][a][b+1][c]+=f[i][j][a][b][c]*p*c,
                 E[i+1][j+1][a][b+1][c]+=E[i][j][a][b][c]*p*c;
                else 
                 f[i+1][j][a][b+1][c-1]+=f[i][j][a][b][c]*p*c,
                 E[i+1][j][a][b+1][c-1]+=E[i][j][a][b][c]*p*c;
            }
           }
        double ans=0;
        for (int i=0;i<=7;i++) 
         for (int a=0;a<=i;a++)
          for (int b=0;b<=i-a;b++) {
            int c=i-a-b;
            ans+=E[k][i][a][b][c];
          }
        printf("%.2lf\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值