【題解】亞瑟王 HNOI 2015 BZOJ 4008 概率 期望 動態規划


傳送門: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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM