zoukankan      html  css  js  c++  java
  • [HNOI2015]亚瑟王

    题目描述

    小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑。他决定,在脱坑之前,最后再来打一盘亚瑟王。既然是最后一战,就一定要打得漂亮。众所周知,亚瑟王是一个看脸的游戏,技能的发动都是看概率的。

    作为一个非洲人,同时作为一个前 OIer,小 K 自然是希望最大化造成伤害的期望值。但他已经多年没写过代码,连 Spaly都敲不对了,因此,希望你能帮帮小 K,让他感受一下当欧洲人是怎样的体验。

    本题中我们将考虑游戏的一个简化版模型。 玩家有一套卡牌,共 n张。游戏时,玩家将 n 张卡牌排列成某种顺序,排列后将卡牌按从前往后依次编号为 1 ~ n。本题中,顺序已经确定,即为输入的顺序。每张卡牌都有一个技能。第 i 张卡牌的技能发动概率为 pi,如果成功发动,则会对敌方造成di点伤害。也只有通过发动技能,卡牌才能对敌方造成伤害。基于现实因素以及小K非洲血统的考虑,pi不会为 0,也不会为 1,即 0 < pi < 1。 一局游戏一共有 r 轮。在每一轮中,系统将从第一张卡牌开始,按照顺序依次考虑每张卡牌。在一轮中,对于依次考虑的每一张卡牌:

    1如果这张卡牌在这一局游戏中已经发动过技能,则

    1.1 如果这张卡牌不是最后一张,则跳过之(考虑下一张卡牌); 否则(是最后一张),结束这一轮游戏。

    2否则(这张卡牌在这一局游戏中没有发动过技能),设这张卡牌为第 i 张

    2.1将其以 pi的概率发动技能。

    2.2如果技能发动,则对敌方造成 di点伤害,并结束这一轮。

    2.3如果这张卡牌已经是最后一张(即 i 等于n),则结束这一轮;否则,考虑下一张卡牌。

    请帮助小 K 求出这一套卡牌在一局游戏中能造成的伤害的期望值。

    输入输出格式

    输入格式:

    输入文件的第一行包含一个整数 T,代表测试数据组数。 接下来一共 T 组数据。 每组数据的第一行包含两个用空格分开的整数 n和r,分别代表卡牌的张数和游戏的轮数。 接下来 n行,每行包含一个实数和一个整数,由空格隔开,描述一张卡牌。第i 行的两个数为 pi和 di,分别代表第 i 张卡牌技能发动的概率(实数)和技能发动造成的伤害(整数)。保证 pi最多包含 4位小数,且为一个合法的概率。

    输出格式:

    对于每组数据,输出一行,包含一个实数,为这套卡牌在这一局游戏中造成的伤害的期望值。对于每一行输出,只有当你的输出和标准答案的相对误差不超过10^-8时——即|a-o|/a<=10-8时(其中a是标准答案,o是输出),你的输出才会被判为正确。建议输出10 位小数。

    输入输出样例

    输入样例#1: 复制
    1
    3 2
    0.5000 2
    0.3000 3
    0.9000 1
    输出样例#1: 复制
    3.2660250000

    说明

    一共有 13 种可能的情况:

    1. 第一轮中,第 1张卡牌发动技能;第二轮中,第 2张卡牌发动技能;

    概率为 0.15,伤害为5。

    1. 第一轮中,第 1张卡牌发动技能;第二轮中,第 3张卡牌发动技能;

    概率为 0.315,伤害为3。

    1. 第一轮中,第 1张卡牌发动技能;第二轮不发动技能;

    概率为 0.035,伤害为2。

    1. 第一轮中,第 2张卡牌发动技能;第二轮中,第 1张卡牌发动技能;

    概率为 0.075,伤害为5。

    1. 第一轮中,第 2张卡牌发动技能;第二轮中,第 3张卡牌发动技能;

    概率为 0.0675,伤害为4。

    1. 第一轮中,第 2张卡牌发动技能;第二轮不发动技能;

    概率为 0.0075,伤害为3。

    1. 第一轮中,第 3张卡牌发动技能;第二轮中,第 1张卡牌发动技能;

    概率为 0.1575,伤害为3。

    1. 第一轮中,第 3张卡牌发动技能;第二轮中,第 2张卡牌发动技能;

    概率为 0.04725,伤害为4。

    1. 第一轮中,第 3张卡牌发动技能;第二轮不发动技能;

    概率为 0.11025,伤害为1。

    1. 第一轮不发动技能;第二轮中,第 1张卡牌发动技能;

    概率为 0.0175,伤害为2。

    1. 第一轮不发动技能;第二轮中,第 2张卡牌发动技能;

    概率为 0.00525,伤害为3。

    1. 第一轮不发动技能;第二轮中,第 3张卡牌发动技能;

    概率为 0.011025,伤害为1。

    1. 第一轮不发动技能;第二轮亦不发动技能;

    概率为 0.001225,伤害为0。

    造成伤害的期望值为概率与对应伤害乘积之和,为 3.266025。

    对于所有测试数据, 1 <= T <= 444, 1 <= n <= 220, 0 <= r <= 132, 0 < pi < 1, 0 <= di <= 1000。

    除非备注中有特殊说明,数据中 pi与di均为随机生成。

    请注意可能存在的实数精度问题,并采取适当措施。

    【spj】

    概率dp显然,然而我不知道怎么定义 转移

    于是一开始写了个深搜 骗了10分,其他点都T了,果然还是太菜了

    在洛谷上看了一篇非常棒的题解,终于开始有点理解了。

    思路:求总期望,根据期望的线性性质,就是求每张牌能发出的概率×每张牌能打出的攻击之和。设每张牌打出的概率为dp[i],由于每轮出牌都是从前向后枚举,易知考虑到此张牌的次数就是总次数减去他前面发出的张数(因为出出一张牌之后就直接进入下一轮,这一轮将不再考虑这之后的牌),由于若没出第一张牌将在每一轮考虑,所以

    首先考虑第一张卡的fpfpfp,也就是fp[0]fp[0]fp[0],应该为

    fp[0]=1−(1−p[i])r\Large fp[0]=1-(1-p[i])^{r}fp[0]=1(1p[i])r

    这个很容易理解,因为(1−p[i])r(1-p[i])^r(1p[i])r就是这张卡从头到尾始终憋着不出的概率

    那么对于后面的fpfpfp应该怎么求呢

    有个条件很烦人,就是在每一轮中,出了一张卡的时候立即结束该轮

    那么下面就轮到dp上场啦!

    f[i][j]f[i][j]f[i][j]表示在所有的rrr轮中,前iii张卡中一共出了jjj张的概率,那么就可以用O(n)O(n)O(n)的时间算出$fpi$

    枚举前i−1i-1i1轮选了jjj张牌,那么有jjj轮不会考虑到第iii张牌,也就是有r−jr-jrj轮会考虑到第iii张牌

    那么根据上面的分析,1−(1−p[i])r−j1-(1-p[i])^{r-j}1(1p[i])rj就是在r−jr-jrj轮中使用过第iii张牌的概率,式子:

    fp[i]=∑j=0rf[i−1][j]⋅(1−(1−p[i])r−j)(i>0)\Large fp[i]=\sum\limits_{j=0}^{r}f[i-1][j]\cdot(1-(1-p[i])^{r-j})(i>0) fp[i]=j=0rf[i1][j](1(1p[i])rj)(i>0)

    接下来只要写出f[i][j]f[i][j]f[i][j]的转移方程就好了,分两种情况讨论

    第一种,f[i][j]f[i][j]f[i][j]从f[i−1][j]f[i-1][j]f[i1][j]转移过来,即第iii张牌最终没有选,始终不选第iii张牌的概率是(1−p[i])r−j(1-p[i])^{r-j}(1p[i])rj

    f[i][j]+=f[i−1][j]⋅(1−p[i])r−j(i>0)\Large f[i][j]+=f[i-1][j]\cdot(1-p[i])^{r-j}(i>0) f[i][j]+=f[i1][j](1p[i])rj(i>0)

    第二种,当j>0j>0j>0时,f[i][j]f[i][j]f[i][j]可以从f[i−1][j−1]f[i-1][j-1]f[i1][j1]转移过来,表示最终选择了第iii张牌

    这时候,有j−1j-1j1轮没有考虑到第iii张牌,所以考虑到第iii张牌的轮数是r−j+1r-j+1rj+1,最终选择的概率为1−(1−p[i])r−j+11-(1-p[i])^{r-j+1}1(1p[i])rj+1

    f[i][j]+=f[i−1][j−1]⋅(1−(1−p[i])r−j+1)(i>0,j>0)\Large f[i][j]+=f[i-1][j-1]\cdot(1-(1-p[i])^{r-j+1})(i>0,j>0) f[i][j]+=f[i1][j1](1(1p[i])rj+1)(i>0,j>0)

    然后就没了,总时间复杂度O(Tnr)O(Tnr)O(Tnr)

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define dd double
    #define N 310 
    using namespace std;
    int t,n,r;
    dd p[N],h[N],dp[N],f[N][N],ans;//dp[i]表示出出第i张牌的概率,f[i][j]表示在r轮中前i张牌取了j张的概率
    dd pow(dd a,int x)
    {
        dd ans=1;
        while(x)
        {
        if(x&1)ans=ans*a;
        a=a*a;
        x>>=1;
        }
        return ans;
    }
    int main()
    {
        scanf("%d",&t);
        while(t--)
        {
        scanf("%d%d",&n,&r);
        memset(dp,0,sizeof(dp));
        memset(f,0,sizeof(f));ans=0;
        for(int i=1;i<=n;i++)scanf("%lf%lf",&p[i],&h[i]);
        dp[1]=1.0-pow((1-p[1]),r);//第一个被打出的概率就是1-r轮后都不打出的概率(因为每一轮都从第一张牌开始决策)
        ans=dp[1]*h[1];
        f[0][0]=1;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=min(i,r);j++)
            {
            f[i][j]+=f[i-1][j]*(pow(1-p[i],r-j));//考虑到第i张牌的r-j轮都不打出
            if(j-1>=0)f[i][j]+=f[i-1][j-1]*(1-pow(1-p[i],r-j+1));//逆向思考
            }
        for(int i=2;i<=n;i++)
        {
            for(int k=0;k<=r;k++)dp[i]+=f[i-1][k]*(1-(pow(1-p[i],r-k)));
            ans+=dp[i]*h[i];
        }
        printf("%.10lf\n",ans);
        }
        return 0;
    }
  • 相关阅读:
    很有意思的“老黄历”网站
    ubuntu
    getopt在Python中的使用
    系统变量TERM不知是用来干什么的?它的值有vt100,vt220等,这些值代表什么意思?
    >/dev/null 2>&1
    linux下常用的ftp服务器软件
    Windows环境下访问NFS
    linux iSCSI target配置全过程
    iSCSI target在安全方面相关设定
    folly学习心得
  • 原文地址:https://www.cnblogs.com/lxykk/p/7726989.html
Copyright © 2011-2022 走看看