Description
osu 是一款群众喜闻乐见的休闲软件。
我们可以把osu的规则简化与改编成以下的样子:
一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。在这个串中连续的 X个1可以贡献X^3 的分数,这x个1不能被其他连续的1所包含(也就是极长的一串1,具体见样例解释)
现在给出n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留1位小数。
Input
第一行有一个正整数n,表示操作个数。接下去n行每行有一个[0,1]之间的实数,表示每个操作的成功率。
Output
只有一个实数,表示答案。答案四舍五入后保留1位小数。
Sample Input
3
0.5
0.5
0.5
Sample Output
6.0
HINT
样例说明:
000分数为0,001分数为1,010分数为1,100分数为1,101分数为2,110分数为8,011分数为8,111分数为27,总和为48,期望为48/8=6.0
N<=100000
Solution
这题求得分的期望,我们很容易想到动态规划。
但这是期望怎样计算呢?概率dp。
假设确定了01序列。我们假设当前位置是0的话,则贡献为0,这个不用计算。否则贡献为
(x+1)3−x3=3x2+3x+1
(其中x为前一个位置最长的全是1的后缀的长度)
注意是贡献,设这个贡献为k,我们要求期望得分(即得分的均值),则f[i] = f[i-1] + k * a[i]; //a[i]是能产生此贡献的概率,是读入的
f[n]就是最终答案。
我们OIER算期望当然不能根据定义傻傻求均值,我们一般用递推方法,通过算概率来求。
根据上面的公式,我们要知道贡献k,必须知道
x
和
我们维护三个数组,
x
的期望l1[],
Summary
对于概率dp、求期望这种数学和dp合二为一的东西我比较少见,但见到就是送分了(因为数学的缺陷连期望都不会求。。),所以一定要多刷此方面的题,总结规律、积累经验。
Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#define N 100010
using namespace std;
int n;
double a[N], l1[N], l2[N], f[N];
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lf", &a[i]);
l1[0] = l2[0] = 0.00;
for(int i = 1; i <= n; i++){
l1[i] = (l1[i-1] + 1) * a[i];
l2[i] = (l2[i-1] + 2 * l1[i-1] + 1) * a[i];
f[i] = f[i-1] + (3 * l2[i-1] + 3 * l1[i-1] + 1) * a[i];
}
printf("%.1lf\n", f[n]);
return 0;
}
该怎么描述我们的相遇,就像某种极低概率的奇迹。