链接
https://www.nowcoder.com/acm/contest/180/B
来源
牛客网
烟花
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
Special Judge, 64bit IO Format: %lld
题目描述
小a有个烟花,每个烟花代表着互不相同的颜色,对于第个烟花,它有的概率点燃,现在小a要去点燃它们,他想知道产生颜色的期望个数 及 产生恰好产生种颜色的概率
输入描述:
第一行两个整数
接下来一行个数,第个数表示第个烟花被点燃的概率
输出描述:
输出有两行
第一行表示产生不同颜色的期望个数
第二行表示产生恰好种颜色的概率
以换行符分割
示例1
输入
3 2
0.5 0.25 0.75
输出
1.5000
0.4062
说明
第二问样例解释:
相加得
备注:
对于的数据:
对于的数据:
输出均保留4位小数,若你的答案误差与std不超过即为正确
题解:由期望的定义可以知道期望=1*p1+1*p2+...+1*pn。求解恰好产生种颜色的概率使用动态规划。dp(i,j)表示使用前i种烟花,让其中j种烟花被点燃的概率。pi表示第i种烟花被点燃的概率,考虑第i种烟花,只有两种选择策略,被点燃或者不被点燃。
策略1:dp(i-1,j)*(1-pi)表示前i-1种烟花中有j种烟花被点燃且第j种烟花不被点燃的概率
策略2:dp(i-1,j-1)*pi表示前i-1种烟花中有j-1种烟花被点燃且第j种烟花被点燃的概率
那么状态转移方程为 dp(i,j)=dp(i-1,j)*(1-pi)+dp(i-1,j-1)*pi
优化空间复杂度:
由dp(i,j)=dp(i-1,j)*(1-pi)+dp(i-1,j-1)*pi 可知只要保证在推dp(i,j)时dp(i-1,j)和dp(i-1,j-1)仍是上一层的结果就行,这和01背包问题一样,只要j从大到小推即可
AC的C++代码:
#include<iostream>
using namespace std;
const int N=100005;
double dp[N],p[N];
int main()
{
int n,k;
double sum=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%lf",&p[i]);
sum+=p[i];
}
dp[0]=1.0;
for(int i=1;i<=n;i++){//考虑前i个物品
for(int j=k;j>=1;j--)//j要从大到小推
dp[j]=dp[j]*(1.0-p[i])+dp[j-1]*p[i];
dp[0]*=(1-p[i]);//dp[0]表示一种都没点燃的概率
}
printf("%.4lf\n%.4lf\n",sum,dp[k]);
return 0;
}