P3239题解

P3269亚瑟王

题意理解

给出 \(n\) 张牌依次使用 \(r\) 轮,其中已使用的不得再次使用,每张牌以 \(p_i\) 的概率使用,使用成功则打出 \(d_i\) 的伤害,求最终伤害的期望。

题目解析

既然要求期望,那么我们可以考虑先求出每张牌打出的概率 \(g_i\),答案即为

\[\sum_{i=1}^n g_id_i \]

于是题目就转为如何求 \(g_i\)

如果是第 \(1\) 张,则每轮有 \(1-p_1\) 的概率不被打出,\(r\) 轮打出的概率就是 \(1-(1-p_1)^r\)
如果是第 \(i\) 张,此时我们肯定不能直接求了,考虑dp。定义一个数组 \(f\)\(f_{i,j}\) 表示前 \(i\) 张牌中,\(r\) 轮打出了 \(j\) 张。那么转移方程如下:
1.如果第 \(i\) 张牌打出,则

\[f_{i,j}=f_{i-1,j-1}(1-(1-p_i)^{r-(j-1)}) \]

2.如果第 \(i\) 张牌没被打出,则

\[f_{i,j}=f_{i-1,j}(1-p_i)^{r-j} \]

因为前 \(i\) 张牌中不可能打出超过 \(i\) 张牌,所以 \((i\ne j)\)

然后用 \(f\)\(g\) 可以用下式:

\[g_i=\sum_{j=0}^{min(i-1,r)} f_{i-1,j}*(1-(1-p_i)^{r-j}) \]

此时,第 \(i\) 张牌的使用概率前 \(i\) 张牌中,第 \(j\) 张使用他的概率之和。

代码

#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=220+5;
int T;
int n,r;
double p[N],pw[N][N];
int d[N];
double f[N][N],g[N];
double ans;
int main()
{
    cin>>T;
    while(T--)
    {
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        memset(pw,0,sizeof(pw));
        cin>>n>>r;
        for(int i=1;i<=n;i++)cin>>p[i]>>d[i];
        for(int i=1;i<=n;i++)pw[i][0]=1;
        if(!r)
        {
            printf("%.10lf\n",0);
            continue;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=r;j++)
                pw[i][j]=pw[i][j-1]*(1.0-p[i]);//预处理(1-p[i])^j
        f[1][0]=pw[1][r];
        f[1][1]=g[1]=1.0-pw[1][r];
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=min(i,r);j++)
            {
                if(j)f[i][j]+=f[i-1][j-1]*(1.0-pw[i][r-j+1]);
                if(i!=j)f[i][j]+=f[i-1][j]*pw[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-pw[i][r-j]);
        ans=0;
        for(int i=1;i<=n;i++)ans+=g[i]*d[i];
        printf("%.10lf\n",ans);
    }
}
posted @ 2026-07-15 14:23  tidewave  阅读(7)  评论(0)    收藏  举报