[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\)

然后考虑第二张牌:

  1. 第一张牌不出现: \(g[2]=1-(1-p[2])^r\)
  2. 第一张牌出现: \(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\) 轮中被发动过的概率。

分发动和不发动两种情况:

  1. 发动:前 \(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]);
  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;
}
posted @ 2021-10-09 17:05  Evitagen  阅读(30)  评论(0编辑  收藏  举报