群巨救我……(期望DP)

群巨救我……(期望DP)

题目链接:https://ac.nowcoder.com/acm/contest/17583/E
题目来源:牛客网

Problem Description

众所周知,牛可乐 的口胡能力十分强大
牛可乐 要讲 n 件事情,我们把这些事情从 1∼n 标号。
牛可乐每讲一件事需要耗费一个单位的时间,但是牛可乐讲事情和普通人不同:牛可乐在讲完 第 i 件事时,只有 p i {p_i} pi的概率继续讲下一件事(第 i+1 件),也就是说,牛可乐讲完第 i 件事后有 (1- p i p_i pi)的概率从第 i-1 件事开始讲!
当牛可乐讲完第 n 件事,并且决定讲下一件事时,牛可乐才算是把这n件事讲完了。
牛妹是个不耐烦的女孩子,她想问问你牛可乐期望要讲多久才能把n件事情全部讲完。

Input

第一行一个整数 n
第二行 n 个浮点数,代表 p i p_i pi,保证 p 1 p_1 p1=1


Output

一行一个浮点数,表示 牛可乐期望要多久才能把全部事情讲完(保留到小数点后 3 位)


Sample Input

4
1 0.6 0.4 0.2

Sample Output

38.333

Remark

对于 20% 的数据,1 ≤ \le n ≤ \le 20
对于 100%的数据,1 ≤ \le n ≤ \le 1 0 5 10^5 105


Solution

这题需要计算数学期望,因为实际上存在无数种情况把n件事情全部讲完,因此普通的计算数学期望的方法并不太可行,此时需要借助动态规划的思想:


f i f_i fi表示从第一件事讲到第i件事所需要的时间。那么显然答案就是 f n f_{n} fn

那么 f i f_i fi会由哪些状态转移呢?

  1. f i f_i fi p i p_i pi的概率:直接由 f i − 1 f_{i-1} fi1转移过来
  2. f i f_i fi 1 − p i 1-p_i 1pi的概率:由 f i − 1 f_{i-1} fi1先转移到 f i − 2 f_{i-2} fi2,再由 f i − 2 f_{i-2} fi2回到 f i f_{i} fi

状态转移方程就该为:
f i = p i − 1 ∗ ( f i − 1 + 1 ) + ( 1 − p i − 1 ) ∗ ( f i − 1 + 1 + f i − f i − 2 ) f_i=p_{i-1}*(f_{i-1}+1)+(1-p_{i-1})*(f_{i-1}+1+f_i-f_{i-2}) fi=pi1(fi1+1)+(1pi1)(fi1+1+fifi2)
其 中 , f i − f i − 2 表 示 从 i − 2 讲 到 i 所 需 的 时 间 其中,f_i-f_{i-2}表示从i-2讲到i所需的时间 ,fifi2i2i
移项化简得:
f i = ( f i − 1 + 1 + ( p i − 1 − 1 ) ∗ f i − 2 ) / p i − 1 f_i=(f_{i-1}+1+(p_{i-1}-1)*f_{i-2})/p_{i-1} fi=(fi1+1+(pi11)fi2)/pi1


Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long ll;

const int N=1e5+100;

int n;
double f[N],p[N];

void solve()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    scanf("%lf",p+i);

    // 公式推导
    // f[i]=p[i-1]*(f[i-1]+1)+(1-p[i-1])*(f[i-1]+1+f[i]-f[i-2])
    // ->  f[i]=(f[i-1]+1+(p[i-1]-1)*f[i-2])/p[i-1]

    f[1]=1;
    for(int i=2;i<=n;i++)
    {
        f[i]=(f[i-1]+1+(p[i-1]-1)*f[i-2])/p[i-1];
    }
    printf("%.3lf\n",f[n]);
}


int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
#endif
    solve();

    /*
    *
    *  ┏┓   ┏┓+ +
    * ┏┛┻━━━┛┻┓ + +
    * ┃       ┃
    * ┃   ━   ┃ ++ + + +
    *  ████━████+
    *  ◥██◤ ◥██◤ +
    * ┃   ┻   ┃
    * ┃       ┃ + +
    * ┗━┓   ┏━┛
    *   ┃   ┃ + + + +Code is far away from  
    *   ┃   ┃ + bug with the animal protecting
    *   ┃    ┗━━━┓ 神兽保佑,代码无bug 
    *   ┃        ┣┓
    *    ┃        ┏┛
    *     ┗┓┓┏━┳┓┏┛ + + + +
    *    ┃┫┫ ┃┫┫
    *    ┗┻┛ ┗┻┛+ + + +
    */
}



最后感谢小伙伴们的学习噢~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Wedream__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值