看着洛谷里那一排任务计划,瑟瑟发抖。。。。。。
题目大意:
你有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.