4008: [HNOI2015]亚瑟王
分析:
根据期望的线性性,直接求出每张牌出现的概率,最后乘以攻击力就是答案。
每张牌出现的概率只与它前面的牌有关,与后面的没有关系,于是按顺序考虑每张牌。
$f[i][j]$表示到第i张牌,还剩j次出牌的机会(即轮数)的概率,那么有$f[0][r] = 1.0$,然后考虑如何转移。
$f[i][j] = f[i - 1][j] imes (1-p[i])^j + f[i - 1][j + 1] imes (1 - (1 - p[i])^{j+1})$
第一项表示第i张牌没有造成伤害的概率,那么后面的轮数中,选到i的时候,概率都是$1-p[i]$,共j轮,所以是$(1-p[i])^j$。
第二项表示第i张牌造成伤害的概率,那么后面的轮中,只要有一轮抽中即可,一共j+1轮,j+1轮都没有的概率是$(1-p[i])^{j +1}$,那么只要抽中一轮即可,就是$1-(1-p[i])^{j +1}$。
第i张牌出现的概率就可以用第二项求出。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<iostream> #include<cctype> #include<set> #include<map> #include<queue> #include<vector> #define fi(s) freopen(s,"r",stdin) #define fo(s) freopen(s,"w",stdout) using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 300; double f[N][N], p[N], mi[N][N]; int a[N]; void solve() { int n = read(), r = read(); for (int i = 1; i <= n; ++i) scanf("%lf%d", &p[i], &a[i]); for (int i = 1; i <= n; ++i) { mi[i][0] = 1.0; for (int j = 1; j <= r; ++j) mi[i][j] = mi[i][j - 1] * (1 - p[i]); } double ans = 0; memset(f, 0, sizeof(f)); f[0][r] = 1.0; for (int i = 1; i <= n; ++i) { double now = 0; for (int j = r; ~j; --j) { f[i][j] = f[i - 1][j] * mi[i][j] + f[i - 1][j + 1] * (1 - mi[i][j + 1]); now += f[i - 1][j + 1] * (1 - mi[i][j + 1]); } ans += 1.0 * now * a[i]; } printf("%.10lf ", ans); } int main () { for (int T = read(); T --; solve()); return 0; }