原题
题目大意
给定 n≤100 个人,每轮随机选取一个人,每个人被选的概率为 pi(精度为0.01),∑pi=1 ,游戏结束当且仅当每个人被抓住一次或以上,问,在最优策略下,期望结束轮数是多少,要求答案精度为 10−6 。
解题思路
设
fi,j
表示第
i
轮结束之后,第
设
gi
表示第
i
轮结束之后,所有人都被抓过的概率。
显然
∵Ans=∑+∞i=1i∗(gi−gi−1)
∴
最优策略就是,尽量使得
i
较小时,
先看看
fi,j
和
fi−1,j
的关系。
1)fi,j=fi−1,j
,第
i
轮不选
2)fi,j=fi−1,j+(1−fi−1,j)∗pj
,第
i
轮选
∴gi=gi−1∗fi,j/fi−1,j
只要求
fi,j/fi−1,j
最大即可,这个可以枚举,或者用数据结构维护。
其实 3∗105 轮过后答案就不会再有大于 10−6 的误差了。
误差分析
gt≥ (1−0.99t/100)100 ≥1−100⋅0.99t / 100.
∑+∞t=N+11−gt≤100∗∑+∞t=N+10.99t/100
.
所以大概
3∗105
次运算之后答案就精准了。
参考代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define maxn 105
#define lim 300000
#define ld long double
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-15
using namespace std;
ld f[2][maxn];
ld g[2],ans;
ld p[maxn];
int n;
int main(){
scanf("%d",&n);
fo(i,1,n) {
cin>>p[i];
p[i]/=100;
}
int last=0,now=1;
fo(i,1,lim) {
last^=1;
now^=1;
ld best=0;
int w=0;
fo(j,1,n) {
ld thi=(1-f[last][j])*p[j]/f[last][j];
if (thi>best) {
best=thi;
w=j;
}
}
fo(j,1,n) {
if (j==w) {
f[now][j]=f[last][j]+(1-f[last][j])*p[j];
}
else {
f[now][j]=f[last][j];
}
}
if (f[last][w]<eps) {
g[now]=1;
fo(j,1,n) g[now]=g[now]*f[now][j];
}
else g[now]=g[last]*f[now][w]/f[last][w];
ans=ans+(g[now]-g[last])*i;
}
double pri=ans;
printf("%.16lf",pri);
return 0;
}