题意
有 n 个 事 件 , 每 个 事 件 发 生 的 概 率 为 p i . 要 求 取 其 中 一 些 事 件 构 成 的 子 集 , 使 得 选 出 来 的 事 件 中 只 发 生 一 件 的 概 率 最 大 . 求 最 大 的 概 率 . 有n个事件,每个事件发生的概率为p_i.\newline 要求取其中一些事件构成的子集,使得选出来的事件中只发生一件的概率最大.\newline 求最大的概率. 有n个事件,每个事件发生的概率为pi.要求取其中一些事件构成的子集,使得选出来的事件中只发生一件的概率最大.求最大的概率.
题解
真的是非常大胆,数据范围 100 100 100的题目标算复杂度 n l o g n nlogn nlogn,佩服一下出题人.
我觉得这题能够大胆贪心的大佬也是胸有成竹,心里非常沉稳.
标算算法把 p p p从大到小排序,选择前缀的事件只发生一件的最大概率即可.
为什么可以这么做?
我们需要最大化
∏ ( 1 − p i ) ⋅ ∑ p i 1 − p i \prod(1-p_i)\cdot\sum\frac{p_i}{1-p_i} ∏(1−pi)⋅∑1−pipi
令 P = ∏ ( 1 − p i ) , S = ∑ p i 1 − p i P=\prod(1-p_i),S=\sum\frac{p_i}{1-p_i} P=∏(1−pi),S=∑1−pipi.
我们假设已经有一个集合,当我们再在这个集合中加入一个发生概率 p i p_i pi的事件的时候,该集合中事件仅发生一件的概率会增加
Δ = − P ⋅ S + P ( 1 − p i ) ⋅ ( S + p i 1 − p i ) = P ⋅ p i ( 1 − S ) \Delta=-P\cdot S+P(1-p_i)\cdot(S+\frac{p_i}{1-p_i})=P\cdot p_i(1-S) Δ=−P⋅S+P(1−pi)⋅(S+1−pipi)=P⋅pi(1−S)
这样的话我们发现当我们加入一个事件的时候,仅有 S < 1 S<1 S<1会导致仅发生一件事的概率增加.
假设我们加入两件发生概率分别为 p i p_i pi和 p j p_j pj的事件.则发生概率变化值之差为
Δ i − Δ j = P ⋅ p i ⋅ ( 1 − S ) − P ⋅ p j ⋅ ( 1 − S ) = P ⋅ ( 1 − S ) ⋅ ( p i − p j ) > 0 \Delta_i-\Delta_j=P\cdot p_i\cdot(1-S)-P\cdot p_j\cdot(1-S)=P\cdot(1-S)\cdot(p_i-p_j)>0 Δi−Δj=P⋅pi⋅(1−S)−P⋅pj⋅(1−S)=P⋅(1−S)⋅(pi−pj)>0.
当 S < 1 S<1 S<1时我们可以得知 p i > p j p_i>p_j pi>pj.
证毕.
最后你需要求前缀概率.
令 p 0 [ i ] p0[i] p0[i]表示前 i i i件事都不发生的概率, p 1 [ i ] p1[i] p1[i]表示前 i i i件事发生一件的概率.
p0[i]=p0[i-1]*(1-p[i]); // 前i-1件事都不发生的概率乘上这件事也不发生的概率.
p1[i]=p0[i-1]*p[i]+p1[i-1]*(1-p[i]); // 前i-1件事都不发生的概率乘这件事发生的概率加上前i-1件事发生一件的概率乘这件事不发生的概率.
好了.
#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int aoi=1018;
typedef double db;
db p[aoi],p0[aoi]={
1.0},p1[aoi];
int main() {
int n=read(),i;
for (i=1;i<=n;++i) scanf("%lf",&p[i]);
sort(p+1,p+n+1,greater<db>());
for (i=1;i<=n;++i)
p0[i]=p0[i-1]*(1-p[i]),
p1[i]=p0[i-1]*p[i]+p1[i-1]*(1-p[i]);
printf("%.9lf\n",*max_element(p1+1,p1+n+1));
}
谢谢大家.