題鏈:
http://www.lydsy.com/JudgeOnline/problem.php?id=4318
題解:
期望dp
如果我們能夠得到以每個位置結尾形成的連續1的長度的相關期望,那麼問題就好解決了。
定義g[i]表示以1位置結尾的連續1的長度的期望。
轉移顯然:g[i]=p[i]*(g[i]+1)
然後定義h[i]表示以1位置結尾的連續1的長度的平方的期望
由於(x+1)^2=x^2+2x+1,
所以h[i]=p[i]*(h[i-1]+2*g[i-1]+1)
最後定義f[i]表示1~i這個區間期望能得到的分數,
分為此時i位置得到1和得到0兩種情況:
得到1,由於(x+1)^3=x^3+3*x^2+3x+1 那麼貢獻為:p[i]*(f[i-1]+3*h[i-1]+3*g[i-1]+1)
得到0,那麼直接為前面的期望得分,貢獻為(1-p[i])*f[i-1]
所以f[i]的轉移為:f[i]=(得到1)p[i]*(f[i-1]+3*h[i-1]+3*g[i-1]+1)+(得到0)(1-p)*f[i-1];
.....................................................................
==,難道沒有感覺這個f[i]的轉移有一絲絲詭異麼?
先看看這個錯的做法,
多了一個d[i],表示以i結尾形成的連續1的長度的3次方的期望。
那麼其轉移類似g和h的轉移:
d[i]=p[i]*(d[i-1]+3*h[i-1]+3*g[i-1]+1)
然後再去求得f[i],同樣地分為當前第i位得到1和得到0兩種情況:
f[i]=(得到1)d[i]+(得到0)(1-p[i])*f[i-1]
乍一看似乎沒問題,但是在(得到1)那裡卻出了問題:
f[i]表示的是1~i這個區間期望能夠得到的分數,
但是在(得到1)這個轉移這裡,我們卻只考慮了以i結尾的期望的那段1的貢獻,然而其它部分的貢獻就沒有轉移過來。
這也就是這個做法得到的答案比正確答案小的原因。
(可以強行把之前的貢獻再加進來麼?233,我反正加不來。。。)
.......................................................................
現在再反過來看看之前正確的f[i]的求法(沒有d[i]陣列的那個做法)
f[i]=(得到1)p[i]*(f[i-1]+3*h[i-1]+3*g[i-1]+1)+(得到0)(1-p)*f[i-1];
顯然(得到0)的那個轉移沒有問題。
那麼我們來想想(得到1)的那麼那個轉移是如何解決掉那個錯誤做法出現的問題的。
由於f[i-1]表示的是區間1~i-1的期望得分,
那麼我們就可以把f[i-1]看成是由兩個部分組成的:
一個部分是以i-1結尾的期望的那段連續的1造成的貢獻A(一個長度的3次方的期望),另一部分則是其它部分的貢獻B:
所以(得到1)這個轉移可以看成是:p[i]*(B+A+3*h[i-1]+3*g[i-1]+1),
顯然,後面的A+3*h[i-1]+3*g[i-1]+1計算的就是以i結尾形成的連續1的長度的3次方的期望,
而B則是其它部分的貢獻。
所以就是這樣巧妙地把新的貢獻和其它部分的貢獻都統計進了f[i]裡面。
以上就是個人的見解。
程式碼:
#include<bits/stdc++.h> #define MAXN 100005 using namespace std; double g[MAXN],h[MAXN],f[MAXN],p; int N; int main(){ ios::sync_with_stdio(0); cin>>N; for(int i=1;i<=N;i++){ cin>>p; g[i]=p*(g[i-1]+1); h[i]=p*(h[i-1]+2*g[i-1]+1); f[i]=p*(f[i-1]+3*h[i-1]+3*g[i-1]+1)+(1-p)*f[i-1]; } cout<<fixed<<setprecision(1)<<f[N]<<endl; return 0; }