HNOI 2015 【亚瑟王】

看着洛谷里那一排任务计划,瑟瑟发抖。。。。。。

题目大意:

你有n张牌,每一张牌有一个发动的概率和造成的伤害值,游戏一共有r轮。对于每一轮游戏,你只能发动一张牌(在之前回合发动过的牌会被跳过,不予考虑),在发动一张牌后这轮游戏结束,进入下一轮。让你求出r轮游戏后,这套牌造成伤害的希望。

输入:t组数据,n张牌,r轮游戏,每张牌在每轮发动的概率p[i],该牌造成的伤害d[i]。

输出:期望伤害值。

思路分析:

注意到期望具有线性性,所以我们可以通过求出每张牌在总共r轮游戏中会发动的概率(g[i]),然后再乘上它的伤害(d[i]),相加,就能得到答案。

即:E=∑ni=1 g[i]*d[i]

那么我们应该如何求出g数组呢?

首先我们可以轻而易举地求出g[1]——g[1]=1-(1-p[1])r,其中(1-p[1])r 就是指第1张牌在r个回合中至始至终都不发动的概率。

那么g[2]怎么求呢?

注意到g[2]发动有2种情况:

        1、第一张牌始终不发动,第二张牌发动。那么此时有r个回合考虑到了第二张牌。

        2、第一张牌发动,第二张牌也发动。那么此时有r-1个回合考虑到了第二张牌。

所以:p[2]=(1-p[1])r*[1-(1-p[2])r]+[1-(1-p[1])r]*[1-(1-p[2])r-1]

发现了什么?

貌似第i张牌发动的概率,只和前i-1牌的发动情况有关,如果前i-1张牌中有j张牌发动过了,那么第i张牌就被考虑了r-j次,发动的概率就为:(前i-1张牌,有j张发动的概率)*[1-(1-p[i])r-j]。

所以:p[i]=∑rj=0 (前i张牌有j张牌发动的概率)*[1-(1-p[i])r-j]

那么我们就新加一个f数组,f[i,j]就表示前i张牌,有j张牌被发动过的概率。

f[i,j]可以由两个状态转移过来:1、f[i-1,j]       2、f[i-1,j-1]

对于f[i-1,j],它想要转移成f[i,j],说明第i张牌没有被发动。所以f[i,j]=f[i,j]+f[i-1,j]*(1-p[i])r-j

对于f[i-1,j-1],他想要转移成f[i,j],说明第i张牌被发动了。所以f[i,j]=f[i,j]+f[i-1,j-1]*(1-(1-p[i])r-j+1)

代码:

var
  pp,f:array[0..220,0..132]of double;
  d:array[1..220]of longint;
  g,p:array[1..220]of double;
  ans:double;
  i,j,t,n,r:longint;
begin
  read(t);
  while t>0 do
  begin
    read(n,r);
    for i:=1 to n do
      read(p[i],d[i]);
    for i:=1 to n do
    begin
      pp[i,0]:=1;                                                //pp数组为预处理出的(1-p[i])任意次方数
      for j:=1 to r do
        pp[i,j]:=pp[i,j-1]*(1-p[i]);
    end;
    f[0,0]:=1;
    for i:=1 to n do
    begin
      f[i,0]:=f[i-1,0]*pp[i,r];
      for j:=1 to r do
        f[i,j]:=f[i-1,j]*pp[i,r-j]+f[i-1,j-1]*(1-pp[i,r-j+1]);
    end;
    fillchar(g,sizeof(g),0); 
    g[1]:=1-pp[1,r]; ans:=g[1]*d[1];
    for i:=2 to n do
    begin
      for j:=0 to r do
        g[i]:=g[i]+f[i-1,j]*(1-pp[i,r-j]);
      ans:=ans+g[i]*d[i];
    end;
    writeln(ans:0:10);
    dec(t);
  end;
end.
原文地址:https://www.cnblogs.com/WR-Eternity/p/9773135.html