题目链接:HDU-5985 Lucky Coins
题意
给出 n 种硬币,已知每种硬币的个数和抛掷后正面朝上的概率,每次将所有硬币抛掷,背面朝上的丢弃,直到只剩下一种硬币被称为幸运硬币,问每种硬币成为幸运硬币的概率。
思路
令 (f[i][k]) 表示第 (i) 种硬币第 (k) 轮之后全无剩下的概率,(p_i) 表示第 (i) 种硬币正面朝上的概率,(n_i) 表示第 (i) 种硬币的个数,则 (f[i][k] = (1-p_i^k)^{n_i}) 。
令 (g[i][k]) 表示第 (i) 种硬币第 (k) 轮之后至少还剩下一个的概率,显然 (g[i][k] = 1-f[i][k]) 。
令 (ans[i]) 表示第 (i) 种硬币成为幸运硬币的概率,则
[ans[i] = sum_{k=0}^infty(g[i][k]-g[i][k+1])prod_{j=1,j
e i}^nf[j][k]
]
后面的连乘表示除了第 (i) 种硬币外,其他的硬币在第 (k) 轮之后全无剩下的概率,(g[i][k]-g[i][k+1]) 表示第 (i) 种硬币在第 (k) 轮之后还有剩下,在第 (k+1) 轮之后全无剩下的概率,即恰好在第 (k) 轮之后成为幸运硬币的概率。如果没有减去 (g[i][k+1]) ,意义是在第 (ksim infty) 轮之后成为幸运硬币的概率,后面再算 (k+1sim infty,k+2sim infty...) 轮,会重复计算。
本题中给定的概率值在 0.4~0.6 之间,当硬币投掷次数很多时,几乎不会对后面的概率产生影响,所以选择能够不超时间空间的近似无穷大值作为 (k) 值的上界即可。
代码实现
#include <cstdio>
double f[11][1010];
double qpow(double a, int b) {
double res = 1;
while (b) {
if (b & 1) res = res * a;
b >>= 1;
a *= a;
}
return res;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, ni, mxk = 1000;
double pi;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d %lf", &ni, &pi);
double pik = 1;
for (int k = 0; k < mxk; k++, pik *= pi) {
f[i][k] = qpow(1 - pik, ni);
}
}
for (int i = 0; i < n; i++) {
double ans = 0;
for (int k = 0; k < mxk - 1; k++) {
double tmp = 1;
for (int j = 0; j < n; j++) {
if (j == i) continue;
tmp *= f[j][k];
}
ans += (f[i][k+1] - f[i][k]) * tmp;
}
printf("%.6f%c", ans, "
"[i==n-1]);
}
}
return 0;
}