• [ HNOI 2015 ] 亚瑟王


    (\)

    (Description)


    (N)张有顺序排好的牌,每张牌有伤害值(v_i)和发动概率(p_i),游戏进行(K)轮,每轮都按顺序扫描,按照以下规则:

    • 若当前牌在前面的轮中未发动过:

      • (p_i)的概率发动,造成(v_i)的伤害,结束该轮;

      • (1-p_i)的概率不发动,扫描下一张牌,若是最后一张就结束该轮;

    • 若当前牌在前面的轮中发动过:

      • 扫描下一张牌,若是最后一张就结束该轮。

    求出这一套卡牌在这(K)轮游戏中能造成的总伤害的期望。 (T)组测试数据。

    • (Tin [1,444],Nin [1,220],Kin[0,132],p_iin[0,1],v_iin[0,1000])

    (\)

    (Solution)


    • 每张牌造成的伤害是固定且独立的,根据期望的线性性,总的期望等于每张牌的期望之和,每张牌的期望等于这张牌发动的概率乘以造成的伤害。而伤害是固定的,所以只需要求出每张牌被发动的概率就好。

    • 这个(DP)思路还是很可以的虽然最后还是抄了题解。他并没有将每一轮分开考虑,而是直接考虑全部的(K)轮。同时,他没有对每一张牌独立考虑,而是对一个前缀的所有牌放到一起考虑。设(f[i][j])表示(K)轮中,前(i)张牌中发动了(j)张的概率。有边界(f[0][0]=1.0),然后递推的时候分为第(i)张牌是否发动考虑:

      • 不发动((j<i)):所以前( i-1)张牌里发动了(j)张,这部分的概率是(f[i-1][j])。然后考虑当前不发动的限制,前(i-1)张中的发动的那(j)张对应的回合里,发动之后这一轮就结束了,所以当前位置没有被考虑到的可能,不发动的概率为(1),而剩下在后(N-i)张里,有(K-j)张要被发动,他们都经过了第(i)张的位置,这些回合中第(i)张牌不能发动,所以有

        [f[i][j]+=f[i-1][j] imes(1-p_i)^{K-j} ]

      • 发动((j ot=0)):前(i-1)张中发动了(j-1)(f[i-1][j-1])。同样的思路那(j)张牌对应的位置不会考虑到当前牌,当前牌在剩下的(K-j+1)轮里挑一轮发动就好了。而容斥起来非常麻烦,用常用的与或转化,不发动是与的条件,所以发动的概率为((1-(1-p_i)^{K-j+1})),所以有

        [f[i][j]+=f[i-1][j-1] imes(1-(1-p_i)^{K-j+1}) ]

    • 考虑如何组合出每一张牌发动的概率,跟第二类转移的思路类似,设(q_i)表示第(i)张牌在这(K)轮中发动的概率,前面发动任意张数都不需要考虑当前位置,在剩下的轮里挑一轮发动就好了

      [q_i=sum_{j=1}^{min(i,K)-1} f[i-1][j] imes (1-(1-p_i)^{K-j}) ]

    • 根据期望的线性性有(E(sum)=sum_{i=1}^n q_i imes v_i)

    (\​)

    (Code)


    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 300
    #define R register
    #define gc getchar
    using namespace std;
     
    inline int rd(){
      int x=0; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
      return f?-x:x;
    }
     
    inline double rdd(){
      double x=0,base=1; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=x*10+(c^48);c=gc();}
      if(c=='.'){
        c=gc();
        while(isdigit(c)){x+=(base/=10)*(c^48);c=gc();}
      }
      return f?-x:x;
    }
     
    int n,m,val[N];
    double p[N],mul[N][N],f[N][N];
     
    inline void work(){
      n=rd(); m=rd();
      for(R int i=1;i<=n;++i) p[i]=rdd(),val[i]=(double)rd();
      for(R int i=1;i<=n;++i){
        mul[i][0]=1.0; f[i][0]=0;
        for(R int j=1;j<=m;++j) f[i][j]=0,mul[i][j]=mul[i][j-1]*(1.0-p[i]);
      }
      f[0][0]=1.0;
      for(R int i=1;i<=n;++i)
        for(R int j=0;j<=min(i,m);++j){
          if(j) f[i][j]+=f[i-1][j-1]*(1.0-mul[i][m-j+1]);
          if(i!=j) f[i][j]+=f[i-1][j]*mul[i][m-j];
        }
      R double ans=0.0,cnt=0.0;
      for(R int i=1;i<=n;++i){
        cnt=0.0;
        for(R int j=0;j<=min(i-1,m);++j) cnt+=f[i-1][j]*(1.0-mul[i][m-j]);
        ans+=cnt*val[i];
      }
      printf("%.10lf
    ",ans);
    }
     
    int main(){
      int t=rd();
      while(t--) work();
      return 0;
    }
    
  • 相关阅读:
    C#使用结构体,输入5个人的学号,姓名,分数,按照成绩高低排列打印出来
    数据库考试试题
    数据库存储系统应用,超市小票系统
    数据库变量与语句
    练习:C#---类(身份证号截取生日、验证邮箱、DateTime)
    Chapter 3. 类---String类、Math类、Datetime类
    练习:C#---for循环(整数和、阶乘、楼梯)
    Chapter 2 C#语句---异常语句
    Chapter 2. C#语句---循环语句
    Chapter 2 C#语句---选择语句
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9715041.html
Copyright © 2020-2023  润新知