(Description)
有(n)张卡牌,每一张卡牌有(p_i)的概率发动,并造成(d_i)点伤害.一共有(r)轮,每一轮按照编号从小到大依次考虑,如果这张牌已经发动过则跳过该牌,否则以(p_i)的概率发动,如果发动成功则造成伤害然后结束该轮,否则跳过这张牌.问期望造成的伤害,(T)组询问
(n<=220,r<=132,T<=444)
(Solution)
这道的答案怎么算应该挺好想的吧.
[sum_{i=1}^n dp[i]*d[i]
]
(dp[i]) 表示第i张牌出现的概率.
但是现在问题就是怎么算(dp)数组啊?
(dp[1])显然啊.为:
[1-(1-p[1])^r
]
((1-p[1])^r)为一直不出的概率.
但是现在好像貌似只能看出来(dp[1])啊 (QAQ)
接续上(dp)了,我们令(f[i][j])为在(r)轮中,前(i)张卡中一共出了(j)张的概率,
至于转移方程,还需要分类讨论一下:
(Case 1:)
(f[i][j])由(f[i-1][j-1])转移
这表示选了第(i)张牌,现在在(r)轮中有(j-1)轮选的是(i)之前的牌,而(i)没有被选到,所以(i)被选到的轮数为(:r-j+1)
转移方程为:
[f[i][j]=f[i-1][j-1]*(1-(1-p[i])^{r-j+1}
]
$ Case 2:( )f[i][j](由)f[i-1][j]$转移而来.
表示不选第i张牌
那么在(r)轮中已经过了(r-j)轮了,剩下的不选(i)的概率为((1-p[i])^{r-j})
所以转移方程为:
[f[i][j]=f[i-1][j]*(1-p[i])^{r-j}
]
现在算出来了(f)数组,接下来就要来算(dp)数组了
[dp[i]=f[i-1][j]*(1-(1-p[i])^{r-j}
]
(Code)
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#define rg register
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
return f*x;
}
int d[1001],r,n;
double f[1001][1001],power[1001][1001],p[1001],dp[1001];
void init(){
memset(f,0,sizeof(f));
memset(dp,0,sizeof(dp));
n=read(),r=read();
for(int i=1;i<=n;i++)
scanf("%lf",&p[i]),d[i]=read();
for(int i=1;i<=n;i++){
power[i][0]=1;
for(int j=1;j<=r;j++)
power[i][j]=power[i][j-1]*(1-p[i]);
}
}
void solve(){
init();
f[1][0]=power[1][r],f[1][1]=dp[1]=1.0-f[1][0];
for(int i=2;i<=n;i++)
for(int j=0;j<=min(i,r);j++){
if(j)
f[i][j]+=f[i-1][j-1]*(1.0-power[i][r-j+1]);
if(i!=j) f[i][j]+=f[i-1][j]*power[i][r-j];
}
for(int i=2;i<=n;i++)
for(int j=0;j<=min(i-1,r);j++)
dp[i]+=f[i-1][j]*(1-power[i][r-j]);
double ans=0;
for(int i=1;i<=n;i++)
ans+=dp[i]*d[i];
printf("%0.10lf
",ans);
}
int main(){
int T=read();
while(T--)
solve();
}