概率dp作业

概率dp特征: 概率DP一般求的是实际结果,在DP过程中,当前状态是由所有子状态的概率共同转移而来,所以概率DP只是利用了DP的动态而没有规划(即只需转移无需决策)。-------qkoqhh

A - Collecting Bugs

题意:一个软件有s个子系统,会产生n种bug。某人一天发现一个bug,这个bug属于某种bug,发生在某个子系统中。

求找到所有的n种bug,且每个子系统都找到bug,这样所要的天数的期望。
需要注意的是:bug的数量是无穷大的,所以发现一个bug,出现在某个子系统的概率是1/s,属于某种类型的概率是1/n。

这题放在第一个,自然是入门题目,这里我在仔细温习下聚聚讲的状态和转移:

1 f[i][j]:表示已经找到i种bug,分j类,找完剩下bug需要天数的期望。
2 转移分4中情况,即分类和系统对于已知和未知的4种组合。
3 相应概率也可以得出。注意4种状态对应的概率求和为1
4 边界:f[n][s]=0 求f[0][0]
5 方程:dp[i][j]=(n-i)/n*(s-j)/s*dp[i+1][j+1]+(n-i)/n*j/s*dp[i+1][j]+i/n*(s-j)/s*dp[i][j+1]+i/n*j/s*dp[i][j]+1

这里方程后面加1因为这是计算下一状态的期望,所以+1。

 1 #include <cstdio>
 2 #include <iostream>
 3 using namespace std;
 4 const int maxn = 1010;
 5 double dp[maxn][maxn];
 6 int main() 
 7 {
 8     //freopen("in.txt", "r", stdin);
 9     int n, s;
10     while(scanf("%d%d", &n, &s) != EOF){
11         dp[n][s] = 0;
12        for(int i=n; i>=0; i--){
13             for(int j=s; j>=0; j--){
14                 if(i==n && j==s)continue;
15                 dp[i][j] =  (dp[i+1][j]*(n-i)*j+dp[i][j+1]*i*(s-j)+dp[i+1][j+1]*(n-i)*(s-j)+n*s)/(n*s-i*j);
16             }
17         }
18         printf("%.4f
", dp[0][0]);
19     }
20     return 0;
21 }
View Code

B - Card Collector

 题意:

集齐n张不同概率(p1,p2……pn)的卡片(一包最多一张)需要买多少包小吃,求期望。

Thinkging:

看数据范围n<=20,可以思考状态压缩(qkoqhh说的)。

这题状态也比较好定义 dp[s]: 已经收集到一些卡牌,状态压缩为s,要收集到所有卡牌的期望。 

这题方程也比较好写

dp[s] =   Σ dp[s]*(q1+q2)           (s>>j & 1)==1  //q1:没有卡片的概率  q2:抽出集合中存在的卡片 
       +  Σ dp[s | 1<<j] * p[j]     (s>>j & 1)==0
 1 #include <cstdio>
 2 #include <cstring>
 3 const int maxn = 21;
 4 double P[maxn];
 5 double dp[1<<maxn];
 6 int main(){
 7 //    freopen("in.txt", "r", stdin);
 8     int n;
 9     while(scanf("%d", &n) != EOF){
10         double p = 0;
11         for(int i=0; i<n; i++){
12             scanf("%lf", &P[i]);
13             p += P[i];
14         }
15         p = 1 - p;   //袋里没有卡片的概率 
16         dp[(1<<n)-1] = 0;
17         for(int s=(1<<n)-2; s>=0; s--){
18             double x = 0, y = 1;
19             for(int j=0; j<n; j++){
20                 if((s>>j) & 1){
21                     x += P[j];
22                 }else{
23                     y += P[j]*dp[s | (1<<j)];
24                 }
25                 dp[s] = y/(1-x-p);
26             }
27         }
28         printf("%.5f
", dp[0]);
29     }
30     return 0;
31 }
View Code

C - Aeroplane chess

题意:在一个从0到n的格子上掷色子,从0点出发,掷了多少前进几步,同时有些格点直接相连,即若a,b相连,当落到a点时直接飞向b点。求走到n或超出n期望掷色子次数。

Thinking:

如果只是摇色子不飞行的话有如下转移:

dp[i]:从i走到n的期望步数
dp[i]= Σ 1/6*dp[i+k]+1    k=1..6

加入飞行方式后两点的期望步数相等

这里可以用并查集维护飞行点的dp转移,注意并查集根部维护的是目标方向的结点

也可以用链式存储转移的关系,同样反向建立链接关系。

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 const int maxn = 100010;
 5 double dp[maxn];
 6 int f[maxn];
 7 int find(int x){
 8     return x==f[x]?x:f[x]=find(f[x]);
 9 }
10 int main(){
11 //    freopen("in.txt", "r", stdin);
12     int n, m;
13     while( scanf("%d%d", &n, &m) != EOF && n){
14         memset(dp, 0, sizeof(dp));
15         memset(f, 0, sizeof(f));
16         for(int i=1; i<=n+6; i++){
17             f[i] = i;
18         }
19         for(int i=0; i<m; i++){
20             int x, y;
21             scanf("%d%d", &x,&y);
22             f[find(x)] = find(y);
23         }
24         for(int i=n-1; i>=0; i--){
25             if(find(i) == i){  //这是不能跳跃的点 
26                 for(int j=i+1; j<=i+6; j++){
27                     dp[i] += (dp[find(j)] + 1)/6;
28                 }
29             }
30         }
31         printf("%.4f
", dp[0]); 
32     }
33 }
View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <vector>
 4 using namespace std;
 5 const int maxn = 100010;
 6 double dp[maxn];
 7 bool vis[maxn];
 8 vector<int> G[maxn];
 9 int main(){
10 //    freopen("in.txt", "r", stdin);
11     int n, m;
12     while( scanf("%d%d", &n, &m) != EOF && (n+m)){
13         memset(dp, 0, sizeof(dp));
14         memset(vis, 0,  sizeof(vis)); 
15         for(int i=0; i<=n; i++)G[i].clear();
16         for(int i=0; i<m; i++){
17             int x, y;
18             scanf("%d%d", &x,&y);
19             G[y].push_back(x); 
20             /*
21              *反向建立链表,存储直接跳转的点对,方便下面逆着转移 
22             */ 
23         }
24         for(int i=0; i<G[n].size(); i++){
25             vis[G[n][i]] = true;
26         }//与终点相连的点可直接跳转 
27         for(int i=n-1; i>=0; i--){
28             if(!vis[i]){  //这是不能跳跃的点 
29                 for(int j=i+1; j<=i+6; j++){
30                     dp[i] += dp[j]/6;
31                 }
32                 dp[i] += 1;
33                 vis[i] = true; 
34             }
35             for(int j=0; j<G[i].size(); j++){
36                 dp[G[i][j]] = dp[i];
37                 vis[G[i][j]] = true;
38             }//可以直接跳转的点 
39         }
40         printf("%.4f
", dp[0]); 
41     }
42     return 0;
43 }
View Code

E - Bag of mice

题意:一个袋子里有n个白老鼠,m个黑老鼠,王子和龙依次取,王子先取,先取到白老鼠的为胜者,其中龙取老鼠的时候,取出一只后,会有随机的一只老鼠跑出来,而且取老鼠的时候,每只老鼠取到的概率是一样的,跑出来的概率也是一样的,算王子赢的概率。

这里学习了kuangbin对这题的解析。看tutorial里用将问题分解为两个子问题(W,B)->(W,B-1)+(W-1,B-1)+(W,B-2),这里可以设置一个标记回合的参数。

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <cstring>
 6 #include <algorithm>
 7 using namespace std;
 8 #define LL long long
 9 
10 const int maxn = 1010;
11 double dp[maxn][maxn];
12 //dp[i][j]:到王妃抓时还剩 i只白鼠j只黑鼠时王妃胜的概率 
13 int main(){
14     //freopen("in.txt", "r", stdin);
15     memset(dp, 0, sizeof(0));
16     int w,b; 
17     scanf("%d%d", &w, &b);
18     for(int i=0; i<=b; i++)dp[0][i]=0;
19     // 没有白鼠时胜率为0 
20     for(int i=1; i<=w; i++)dp[i][0]=1;
21     //没有黑鼠时 胜率为1
22     for(int i=1; i<=w; i++){
23         for(int j=1; j<=b; j++){
24             dp[i][j] += (double)i/(i+j);//王妃抓到一只白鼠
25             if(j>=3){
26                 dp[i][j] += ((double)j/(i+j)) * ((double)(j-1)/(i+j-1)) * ((double)(j-2)/(i+j-2)) * dp[i][j-3];
27             }//王妃抓到黑,龙抓到一个黑,放了一个黑 
28             if(j>=2){
29                 dp[i][j] += ((double)j/(i+j)) * ((double)(j-1)/(i+j-1)) * ((double)i/(i+j-2)) * dp[i-1][j-2];
30             }//王妃抓到黑,龙抓到黑,放了一个白 
31         }
32     } 
33     printf("%.9lf
", dp[w][b]);
34     return 0;
35 }
View Code
 1 double dfs(int w, int b, int flag){
 2     if(w == 0) return flag;  //flag=1:此轮龙抓,龙胜返回1; 代表两人都没抓到白鼠,龙胜 
 3     if(b == 0) return 1;
 4     double &memo = dp[w][b][flag];
 5     if(memo > -0.5) return memo;
 6     double ans = 0;
 7     if(flag == 0){
 8         ans += (double)w/(w + b);   //王妃抓白鼠胜 
 9         ans += ((double)b/(w + b)) * (1 - dfs(w, b-1, 1));
10         //王妃抓黑鼠,下一轮龙抓失败的概率(dfs()返回成功的概率) 即王妃胜 
11     }else{
12         ans += (double)w/(w + b);   //龙抓白鼠胜的概率
13         ans += ((double)b/(w + b)) * ((double)w/(w+b-1)) * (1 - dfs(w-1, b-1, 0));
14         // 龙抓一个黑鼠放一个白鼠胜的概率 
15         if(b > 1) { //龙抓一个黑鼠 放一个黑鼠胜的概率 
16             ans += ((double)b/(w + b)) * ((double)(b-1)/(w + b-1)) * (1 - dfs(w, b-2, 0));
17         }
18     }
19     return memo = ans;
20 }
21 dp[i][j][0]=dp[i][j][1] = -1
原文地址:https://www.cnblogs.com/seaupnice/p/9482175.html