osu合集(期望dp)
T1
我们设\(f_i\)表示到\(i\)的连续个数平方的期望。 \(g_i\)表示到到\(i\)的连续个数的期望
在维护\(f_i\)的同时维护一下\(g_i\)就行了。
转移方程: \(g_i\)= \(p_i \times g_{i-1}\);
\(f_i = p_i \times (f_{i-1} + 2 \times g_{i-1} + 1) + (1-p_i) \times f_{i-1}\)
解释一下第二个方程,
长度有\(p_i\)的概率有\(x\)变为\(x+1\)
那么分数就会增加\((x+1)^2 - x^2\) 即\(2 \times x - 1\)
再加上有\(1-p[i]\)的概率保持原来的不变,所以就有了上面的方程
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
char ch;
int n;
double p,g[300010],f[300010];
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
cin>>ch;
if(ch == 'o') p = 1.0;
if(ch == 'x') p = 0.0;
if(ch == '?') p = 0.5;
g[i] = p * (g[i-1] + 1);//维护一下区间期望长度
f[i] = f[i-1] * (1-p) + (f[i-1] + 2 * g[i-1] + 1) * p;//维护期望得分
}
printf("%.4f\n",f[n]);
return 0;
}
T2
和上面那个题差不多。
转移时维护一个连续个数的期望和连续个数平方的期望就水过去了。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
double p,f[100010],g[100010];
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%lf",&p);
g[i] = p * (g[i-1] + 1);
f[i] = f[i-1] * (1-p) + (f[i-1] + 2 * g[i-1] + 1) * p;
}
printf("%.15lf",f[n]);
return 0;
}
T3
这个题很上面的两个题还是有点区别的,但是多想一想也就明白了
当连续的X个个数由\(x\)变为\(x+1\)时
期望得分就会由\(x^3\)变为\((x+1)^3\)
也就是\(3 \times x^2 \times \ 3 \times x + 1\)
转移的时候维护三个数组,分别表示连续个数的期望,连续个数平方的期望,和期望得分。
剩下的随便水水就过了。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
double p[100010],f[100010],g[100010],h[100010];
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++) scanf("%lf",&p[i]);
for(int i = 1; i <= n; i++)
{
h[i] = p[i] * (h[i-1] + 1);//连续个数的期望
g[i] = p[i] * (g[i-1] + 2 * h[i-1] + 1);//连续个数平方的期望
f[i] = f[i-1] * (1-p[i]) + p[i] * (f[i-1] + 3 * g[i-1] + 3 * h[i-1] + 1);//期望得分
}
printf("%.1lf",f[n]);
return 0;
}
期望题就是想出来与想不出来的区别。
想出来了代码也就可以实现出来了。
但是想不出来他就是 爆玲QAQ.