傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=4008
一道不簡單的概率和期望dp題
根據期望的線性性質,容易想到,可以算出每張卡的期望傷害,然后全部加在一起
手算樣例之后發現是正確的,那么我們只要求出每張卡的實際被使用的概率就可以了
記第$i$張卡的實際被使用的概率為$fp[i]$
那么答案就是
$\Large\sum\limits_{i=0}^{n-1}fp[i]\cdot d[i]$
如何求出$fp[i]$?
首先考慮第一張卡的$fp$,也就是$fp[0]$,應該為
$\Large fp[0]=1-(1-p[i])^{r}$
這個很容易理解,因為$(1-p[i])^r$就是這張卡從頭到尾始終憋着不出的概率
那么對於后面的$fp$應該怎么求呢
有個條件很煩人,就是在每一輪中,出了一張卡的時候立即結束該輪
那么下面就輪到dp上場啦!
令$f[i][j]$表示在所有的$r$輪中,前$i$張卡中一共出了$j$張的概率,那么就可以用$O(n)$的時間算出$fp[i](i>0)$
枚舉前$i-1$輪選了$j$張牌,那么有$j$輪不會考慮到第$i$張牌,也就是有$r-j$輪會考慮到第$i$張牌
那么根據上面的分析,$1-(1-p[i])^{r-j}$就是在$r-j$輪中使用過第$i$張牌的概率,式子:
$\Large fp[i]=\sum\limits_{j=0}^{r}f[i-1][j]\cdot(1-(1-p[i])^{r-j})(i>0)$
接下來只要寫出$f[i][j]$的轉移方程就好了,分兩種情況討論
第一種,$f[i][j]$從$f[i-1][j]$轉移過來,即第$i$張牌最終沒有選,始終不選第$i$張牌的概率是$(1-p[i])^{r-j}$
$\Large f[i][j]+=f[i-1][j]\cdot(1-p[i])^{r-j}(i>0)$
第二種,當$j>0$時,$f[i][j]$可以從$f[i-1][j-1]$轉移過來,表示最終選擇了第$i$張牌
這時候,有$j-1$輪沒有考慮到第$i$張牌,所以考慮到第$i$張牌的輪數是$r-j+1$,最終選擇的概率為$1-(1-p[i])^{r-j+1}$
$\Large f[i][j]+=f[i-1][j-1]\cdot(1-(1-p[i])^{r-j+1})(i>0,j>0)$
然后就沒了,總時間復雜度$O(Tnr)$,具體細節看代碼
因為洛谷上有點卡時,所以預處理了$(1-p[i])$的冪
1 #include <cstring> 2 #include <algorithm> 3 #include <cstdio> 4 5 using namespace std; 6 const int MAXN = 223; 7 const int MAXR = 135; 8 9 int n, r, d[MAXN]; 10 double p[MAXN], fp[MAXN]; 11 12 double pow1p[MAXN][MAXN]; // pow1p[i][j]表示(1-p[i])^j 13 void prelude() { 14 for( int i = 0; i < n; ++i ) { 15 pow1p[i][0] = 1; 16 for( int j = 1; j <= r; ++j ) 17 pow1p[i][j] = pow1p[i][j-1] * (1-p[i]); 18 } 19 } 20 21 double f[MAXN][MAXR]; 22 double solve() { 23 memset( f, 0, sizeof(f) ); 24 memset( fp, 0, sizeof(fp) ); 25 f[0][0] = pow1p[0][r]; // 邊界 26 f[0][1] = fp[0] = 1-f[0][0]; 27 for( int i = 1; i < n; ++i ) { 28 for( int j = 0; j <= r; ++j ) { 29 fp[i] += f[i-1][j] * (1 - pow1p[i][r-j]); // 根據f計算fp 30 f[i][j] += f[i-1][j] * pow1p[i][r-j]; // 不選第i張 31 if( j ) f[i][j] += f[i-1][j-1] * (1 - pow1p[i][r-j+1]); // 選第i張 32 } 33 } 34 double rtn = 0; 35 for( int i = 0; i < n; ++i ) { 36 // printf( "fp[%d] = %lf\n", i, fp[i] ); 37 rtn += d[i] * fp[i]; 38 } 39 return rtn; 40 } 41 42 int main() { 43 int T; scanf( "%d", &T ); 44 while( T-- ) { 45 scanf( "%d%d", &n, &r ); 46 for( int i = 0; i < n; ++i ) scanf( "%lf%d", p+i, d+i ); 47 prelude(); 48 printf( "%.10lf\n", solve() ); 49 } 50 return 0; 51 }