[HNOI2015] 亚瑟王 题解

前言

题目链接:洛谷

题意简述

一套按顺序的 n 张卡牌,每张卡牌都有一个技能,第 i 张卡牌的技能发动概率为 pi,如果成功发动,则会对敌方造成 di 点伤害,并弃置这张牌。一局游戏一共有 r 轮,或者当手中没有任何一张牌的时候结束。在每一轮中,按照顺序依次考虑剩下的卡牌,如果成功发动,那么对敌方造成伤害,弃置它,进入下一轮,否则考虑下一张牌,或者当这张牌就是最后一张牌时,进入下一轮。求这套卡牌在一局游戏中能造成的伤害的期望值。

1n2200r1320<pi<10di1000

题目分析

状压 DP 可以拿到部分分,但是没有任何前途。

显然可以对计数视角转化,考虑每一张牌在这一局游戏中,被打出的概率 Pi,那么答案可以表示为 Pidi。考虑如何求 Pi

我们发现对于第一张牌 P1 很容易求,就是 1(1p1)r

我不会求 P1

考虑它没有被打出,当且仅当每一次考虑到它时,都以 1p1 的概率没有打出,那么没有被打出的概率就是 (1p1)r,对立事件即被打出了的概率即为 1(1p1)r

或者有人将它理解为一个 01 序列,每一位独立且有 p1 的概率为 11(1p1)r 表示至少存在一个 1 的概率,似乎这是不对的。考虑一个存在多个 1 的局面,其中第一个 1i,那么我们会统计到 i+1n000000 取遍 111111 的任意局面,这些概率之和为 1,那么我们相当于统计到了 1i 形成 000001 的局面然后马上停下的概率,也就是我们要求的。

发现其他 Pi 似乎不好直接列式子,我们希望它也可以使用形如 1(1pi)k 的表达式来计算,其中 k 表示在 r 轮中,有 k1i1 都没有出牌,轮到了 i 的判定。但是 k 显然不是定值,那么我们就枚举 k,这样就需要知道恰有 k1i1 都没有出牌的概率。这个似乎可以 DP。设 fi,j 表示前 i 张牌中,出了 j 张牌的概率,于是有 rj1i1 都没有出牌,即 k=rjPi=j=0rfi1,j(1(1pi)rj)

接下来问题变成了如何求 fi,j。边界为 f1,0=(1p1)rf1,1=1f1,0。从 fi1 转移到 fi,j,第 i 张牌在 r 轮中,可能出牌了,也可能没有出牌,分两种情况转移:

  1. i 出牌了。
    fi1,j1 转移而来。那么会有 r(j1) 次判定到 ii 出牌的概率为 1(1pi)rj+1

    fi,jfi1,j1(1(1pi)rj+1)

  2. i 没有出牌。
    fi1,j 转移而来。那么会有 rj 次判定到 ii 每次都不出的概率为 (1pi)rj

    fi,jfi1,j(1pi)rj

于是可以做到 O(nr) 的时间复杂度解决本题。

代码

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 225;
const double eps = 1e-13;
int n, r, d[N];
double p[N];
double pw[N][N], f[N][N], P[N];
void solve() {
scanf("%d%d", &n, &r);
for (int i = 1; i <= n; ++i)
scanf("%lf%d", &p[i], &d[i]);
for (int i = 1; i <= n; ++i) {
pw[i][0] = 1;
for (int j = 1; j <= r; ++j) {
pw[i][j] = pw[i][j - 1] * (1 - p[i]);
f[i][j] = 0;
}
}
f[1][0] = pw[1][r], f[1][1] = P[1] = 1 - f[1][0];
for (int i = 2; i <= n; ++i)
for (int j = 0; j <= min(i, r); ++j) {
f[i][j] = f[i - 1][j] * pw[i][r - j];
if (j) f[i][j] += f[i - 1][j - 1] * (1 - pw[i][r - j + 1]);
}
for (int i = 2; i <= n; ++i) {
P[i] = 0;
for (int j = 0; j <= min(i - 1, r); ++j)
P[i] += f[i - 1][j] * (1 - pw[i][r - j]);
}
double ans = 0;
for (int i = 1; i <= n; ++i)
ans += P[i] * d[i];
printf("%.10lf\n", ans + eps);
}
signed main() {
int t; scanf("%d", &t);
while (t--) solve();
return 0;
}
posted @   XuYueming  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示