[HNOI2015]亚瑟王
[HNOI2015]亚瑟王
题意:
一共 \(n\) 张牌 \(r\) 轮,给定每张牌在每一轮中出现的概率 \(p[i]\) 和权值 \(d[i]\) 。
若一张牌出现,则进入下一轮,这张牌无效。
若所有牌遍历完,也进入下一轮。
问期望的权值之和。
分析:
因为期望是线性的,因此我们设 \(g[i]\) 为 \(i\) 这张牌在 \(r\) 轮中出现的概率,则有公式:
\[ans=\sum_{i=1}^n g[i] \times d[i]
\]
我们考虑如何算概率:
考虑只有第一张牌出现的概率:\(g[1]=1-(1-p[1])^r\)
然后考虑第二张牌:
- 第一张牌不出现: \(g[2]=1-(1-p[2])^r\)
- 第一张牌出现: \(g[2]=1-(1-p[2])^{r-1}\)
对于任意的 \(i>1\),在第 \(1\) 张牌到第 \(i−1\) 张牌在所有 \(r\) 轮内是否算权值已经确定的情况下,第 \(i\) 张牌被发动技能的概率只取决于第 \(1\) 张牌到第 \(i−1\) 张牌中有多少张发动了技能。
因此,我们设 \(gl[i][j]\) 表示 \(i\) 在 \(j\) 轮中不发生的概率.
进行 \(dp\) 转移: \(f[i][j]\) 表示前 \(i\) 张牌,恰好有 \(j\) 张在所有 \(r\) 轮中被发动过的概率。
分发动和不发动两种情况:
- 发动:前 \(i-1\) 张牌有 \(j-1\) 张牌算权值,因此第 \(i\) 张牌,在 \(r\) 轮中有 \(j-1\) 次不会算权值,则有转移方程:
if(j) f[i][j]+=f[i-1][j-1]*(1.0-gl[i][r-j+1]);
- 不发动:那么前 \(i−1\) 张牌中一定有 \(j\) 张牌算权值,因此对于第 \(i\) 张牌,在 \(r\) 轮中有 \(j\) 轮不会算权值
if(i!=j) f[i][j]+=f[i-1][j]*gl[i][r-j];
最后,\(g[i]\) 的方程就是:
\[g_i=\sum_{j=0}^{min(i-1,r)} (1-(1-p[i])^{r-j})f[i-1][j]
\]
也就是:
g[i]+=f[i-1][j]*(1.0-gl[i][r-j]);
最后输出 \(ans\) 即可。
代码:
#include<bits/stdc++.h>
#define db double
using namespace std;
const db eps=1e-8;
const int N=235;
int n,r,T,d[N];
db p[N];
db f[N][N],g[N],gl[N][N];
//f[i][j] 表示前 i 张牌,恰好有 j 张在所有 r 轮中被发动过的概率
int main(){
cin>>T;
while(T--){
cin>>n>>r;
memset(g,0,sizeof(g)); memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
scanf("%lf%d",&p[i],&d[i]); gl[i][0]=1;
}
if(!r){puts("0.0000000000"); continue;}
for(int i=1;i<=n;i++)
for(int j=1;j<=r;j++)
gl[i][j]=gl[i][j-1]*(1-p[i]);//i在j轮中不发生的概率
f[1][0]=gl[1][r];//第一张牌不出现的概率
f[1][1]=g[1]=1.0-gl[1][r];//第一张牌出现的概率
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-gl[i][r-j+1]);
if(i!=j) f[i][j]+=f[i-1][j]*gl[i][r-j];
}
for(int i=2;i<=n;i++)
for(int j=0;j<=min(i-1,r);j++)
g[i]+=f[i-1][j]*(1.0-gl[i][r-j]);
double ans=0;
for(int i=1;i<=n;i++) ans+=g[i]*d[i];
printf("%.10lf\n",ans);
}
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9