AtCoder Educational DP Contest I - Coins 题解
发表于|更新于
|阅读量:
题意
有n枚硬币,第i枚硬币有pi的概率正面朝上,有1−pi的概率反面朝上。
扔完所有硬币,求正面朝上的银币数比反面朝上的银币数多的概率。
思路
是一道期望dp的入门题,其实比起dp把他叫做递推更合适。
状态设计
这道题的状态设计需要仔细思考,将状态f[i][j]设为前i个硬币中有j个以上正面朝上的硬币显然不好,转移过于复杂。
于是我们考虑将状态设计为前i个硬币中正好有j个正面朝上的硬币,最后再将f[n][2⌈n+1⌉]至f[n][n]的值累加起来即可。
转移方程
那状态转移方程如何编写呢?一个硬币i的状态一定只有两种可能,有pi的概率正面朝上,1−pi的概率反面朝上。
要让前i个硬币里有正好j个正面朝上,也就有两种可能:
- 前i−1个硬币里有j−1个正面朝上,并且第i个正面朝上,概率为f[i−1][j−1]×p[i];
- 前i−1个硬币里已经有j个正面朝上,此时第i个只能反面朝上,概率为f[i−1][j]×(1−p[i])。
初始状态
现在状态和状态转移方程我们都设计好了,来考虑初始状态的值。
我们转移的时候总会用到f[i−1][j]和f[i−1][j−1],所以我们需要把每一项f[i][0]都设置好。显然前0个硬币里有0个正面朝上的概率是1,后面的f[i][0]自然就等于f[i−1][0]×(1−p[i]),因为所有硬币都必须反面朝上。
到这里题目我们就做完了,要注意精度的问题。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <bits/stdc++.h> using namespace std; using ld = long double; ld p[3005]; ld ans[3005][3005]; int main(){ int n;cin>>n; for(int i=1;i<=n;i++){cin>>p[i];} for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) ans[i][j] = 0.00; } ans[0][0] = 1; for(int i=1;i<=n;i++){ ans[i][0] = ans[i-1][0]*(1-p[i]); for(int j=1;j<=i;j++){ ans[i][j] = ans[i-1][j-1]*p[i]+ans[i-1][j]*(1-p[i]); } } ld sum = 0; for(int i=(n+1)/2;i<=n;i++){ sum+=ans[n][i]; } cout<<fixed<<setprecision(10)<<sum; return 0; }
|