概率dp专辑

求概率

uva11021 http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1962

给定n,k,m

有k个麻雀,每只活一天就会死,临死之前生出i只麻雀的概率为pi ,  0<= i <n

问m天后,麻雀死光的概率

独立事件同时发生是每个事件的概率相乘, 每只麻雀都是独立的,只要求出一只麻雀m天后死亡的概率dp[m], 那么k只麻雀m天后死亡的概率为dp[m]^k

dp[i]表示i天后麻雀全部死亡的概率, 这个全部死亡即自己死亡,后代也死亡

dp[i]可以分解为n个子事件,生0->n-1个孩子,如果生j个孩子,那么j个孩子要在i-1天后死亡,这样全部的麻雀才会在i天后死亡,j个孩子要在i-1天后死亡是独立事件同时发生

所以是dp[i-1]^j,生j个孩子的概率为pj, 所以生j个孩子且i-1天后死亡也是独立事件,概率为pj * dp[i-1]^j

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 typedef long long LL;                   
16 const int INF = 1<<30;
17 /*
18 f[i] = p0
19 */
20 const int N = 1000 + 10;
21 double p[N],dp[N];
22 int main()
23 {
24     int n, k, m, t, tCase = 1;
25     scanf("%d", &t);
26     while (tCase <= t)
27     {
28         scanf("%d%d%d", &n, &k, &m);
29         for (int i = 0; i < n; ++i)
30             scanf("%lf", &p[i]);
31         dp[0] = 0;//0天后死亡是不可能的,所以概率是0
32         dp[1] = p[0];//一天后死亡的概率是不生孩子的概率
33         for (int i = 2; i <= m; ++i)
34         {
35             dp[i] = 0;
36             for (int j = 0; j < n; ++j)
37                 dp[i] += p[j] * pow(dp[i - 1], j);
38         }
39         printf("Case #%d: %.7lf\n", tCase++, pow(dp[m], k));
40     }
41     return 0;
42 }
View Code

 

http://acm.hit.edu.cn/hoj/problem/view?id=2866

给定n,m 表示第一个人的血量和第二个人的血量

然后接下来两行,每行6个数字,分别表示摇骰子得到点数1->6的概率

要我们求第一个人赢的概率

p1,p2,p 表示进行一次游戏,第一个人赢,第二个人赢,平局的概率
q1,q2表示前n局中,第一个人赢一局,其他都是平局, 第二个人赢一局,其他都是平局的概率,如图

q1 = p1/(1-p)
q2 = p2/(1-p)

dp[i][j] 表示第一个人赢i次,第二个人赢j次, dp[i][j] = dp[i-1][j]*q1 + dp[i][j-1]*q2
为什么是dp[i][j] = dp[i-1][j]*q1 + dp[i][j-1]*q2;
而不是 dp[i][j] = dp[i-1][j]*p1 + dp[i][j-1]*p2;
因为进行一局游戏,有第一个人赢,第二个人赢,平局
不可能理想到只进行了一次游戏,就可能第一个人赢 即dp[i-1][j]*p1,
所以要一个人赢一局,可能经过了n局,然后才赢一局, q1,q2就是经过了很多平局,才赢得一局的情况
答案是dp[hp2][0->hp1-1]
初始化条件是dp[0][0] = 1,这是必然的,因为刚开始的,必定两个人都没有赢过一次,所以是1,必定发生

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 typedef long long LL;                   
16 const int INF = 1<<30;
17 /*
18 
19 */
20 double dp[2001][2001];
21 int main()
22 {
23     int hp1, hp2;
24     double a[6], b[6];
25     int i, j;
26     double p1, p2, p, q1, q2;
27     while (scanf("%d%d", &hp1, &hp2) != EOF)
28     {
29         p1 = p2 = p = q1 = q2 = 0;
30         for (i = 0; i < 6; ++i)
31             scanf("%lf", &a[i]);
32         for (i = 0; i < 6; ++i)
33             scanf("%lf", &b[i]);
34         for (i = 0; i < 6; ++i)
35         for (j = 0; j < i; ++j)
36         {
37             p1 += a[i] * b[j];
38             p2 += a[j] * b[i];
39         }
40         p = 1.0 - p1 - p2;
41         p == 1 ? q1 = q2 = 0 : q1 = p1 / (1 - p), q2 = p2 / (1 - p);
42         //q1 = p1 / (1.0 - p);
43         //q2 = p2 / (1.0 - p);
44         memset(dp, 0, sizeof(dp));
45         dp[0][0] = 1.0;
46         for (i = 0; i <= hp2; ++i)
47         {
48             for (j = 0; j <= hp1; ++j)
49             {
50                 if (j<hp1 && i) dp[i][j] += dp[i - 1][j] * q1;
51                 if (i<hp2 && j) dp[i][j] += dp[i][j - 1] * q2;
52             }
53         }
54         double ans = 0;
55         for (j = 0; j < hp1; ++j)
56             ans += dp[hp2][j];
57         printf("%.6lf\n", ans);
58     }
59     return 0;
60 }
View Code

 

poj 2151 http://poj.org/problem?id=2151

m t n
m到题目, t个队伍, n 冠军队最少解决n道题
t行,每行m个数字
表示每个队伍解决第i道题目的概率

问我们每个队伍至少解决一题,且冠军队至少解决n题的概率
p1为每个队伍至少解决一题的概率
p2为每个队伍解决k题的概率 1<=k<n
最终答案为p1-p2,每个队伍至少做出一题,且冠军队至少解决n题的概率

单独算出队伍至少解决k题的概率,每只队伍的概率相乘(独立事件同时发生)
dp[i][j][k] 为第i只队伍前j道题目解出k题的概率 dp[i][j][k] = dp[i][j-1][k-1] * p[i][j] + dp[j-1][k] *(1-p[i][j])

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 typedef long long LL;                   
16 const int INF = 1<<30;
17 /*
18 
19 */
20 double p[1000+10][30+10];
21 double dp[30 + 10][30 + 10]; 
22 double s[33];
23 int main()
24 {
25     int n, m, t, i, j, k;
26     while (scanf("%d%d%d", &m, &t, &n), m+n+t)
27     {
28         double p1, p2, tmp;
29         p1 = p2 =  1;
30         for (i = 1; i <= t; ++i)
31         {
32             tmp = 1;
33             for (j = 1; j <= m; ++j)
34             {
35                 scanf("%lf", &p[i][j]);
36                 tmp = tmp * (1 - p[i][j]);//tmp为一题都没做出来的概率
37             }
38             p1 = p1*(1 - tmp);//p1为每个队伍至少做出一题的概率
39         }
40         for (i = 1; i <= t; i++)
41         {
42             //dp[0][0] = 1;
43             memset(dp, 0, sizeof(dp));
44             dp[0][0] = 1;
45             //for (j = 1; j <= m; ++j)
46             //    dp[j][0] = dp[j - 1][0] * (1 - p[i][j]);
47             for (j = 1; j <= m; ++j)
48             {
49                 dp[j][0] = dp[j - 1][0] * (1 - p[i][j]);
50                 for (k = 1; k <=j; ++k)
51                 {
52                     dp[j][k] += dp[j - 1][k] * (1 - p[i][j]);
53                     //if (k!=0)
54                     dp[j][k] += dp[j - 1][k - 1] * p[i][j];
55                 }
56             }
57             tmp = 0;
58             for (k = 1; k < n; ++k)
59                 tmp += dp[m][k];
60             p2 = p2 * tmp;
61         }
62         printf("%.3f\n", p1 - p2);
63     }
64     return 0;
65 }
View Code

 

poj3071 http://poj.org/problem?id=3071

给定一个n, 表示有2^n个队伍进行比赛

给定一个2^n * 2^n的矩形

pij 表示第i队伍打败第j只队伍的概率

比赛的规则是第一只队伍和第二只打,第三只队伍和第四只打,赢的晋级,然后还是依照这样的规则,如图

要我们求哪只队伍最终获胜的概率最大,输出该队伍

dp[i][j] 表示第i次比赛,队伍j获胜,   设k为要与j比赛的队伍    dp[i][j] += sum(dp[i-1][j] * dp[j-1][k] * p[j][k] ) 

那么怎么判断所要与j比赛的,我们对队伍进行分组, 队伍号/(i<<(i-1)) 就是组号了,  如果是组号是偶数,那么要与后一只队伍比赛,如果是奇数,那么要与前一只队伍比赛

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 typedef long long LL;                   
16 const int INF = 1<<30;
17 /*
18 
19 */
20 double dp[8][256], p[256][256];
21 int main()
22 {
23     int n, i, j, k;
24     while (scanf("%d", &n), n != -1)
25     {
26         int m = 1 << n;
27         for (i = 0; i < m; ++i)
28         for (j = 0; j < m; ++j)
29             scanf("%lf", &p[i][j]);
30         for (i = 0; i < m; ++i)
31             dp[0][i] = 1;//dp[i][j]表示第i次比赛,获胜者是j的概率
32         for (i = 1; i <= n; ++i)//2^n个队伍每次淘汰一半的队伍,所以要进行n次比赛才能决出胜负
33         {
34             int t = 1 << (i-1);
35             for (j = 0; j < m; ++j)
36             {
37                 dp[i][j] = 0;
38                 int teamNumWin = j / t;//分组
39                 for (k = 0; k < m; ++k)
40                 {
41                     int teamNumLose = k / t;//分组
42                     if (teamNumWin % 2 == 0 && teamNumWin + 1 == teamNumLose)//如果组数是偶数,那么与后一组比赛
43                         dp[i][j] += dp[i - 1][j] * dp[i - 1][k] * p[j][k];
44                     if (teamNumWin % 2 == 1 && teamNumWin - 1 == teamNumLose)//如果组数是奇数,与前一组比赛
45                         dp[i][j] += dp[i - 1][j] * dp[i - 1][k] * p[j][k];
46                 }
47             }
48 
49         }
50         double Max = 0;
51         int ans;
52         for (i = 0; i < m; ++i)
53         if (dp[n][i]>Max)
54         {
55             Max = dp[n][i];
56             ans = i;
57         }
58         printf("%d\n", ans + 1);
59     }
60     return 0;
61 }
View Code

 

http://codeforces.com/problemset/problem/148/D

给定w,b 分别为袋子里白老鼠和黑老鼠的数量

先取到白老鼠的人赢,一轮游戏后,即两个人各取了一只后,会从带子里随机逃出一只老鼠,老鼠逃出的概率是均等的。

问第一个人赢的概率

dp[i][j] 表示有i只白鼠,j只黑鼠的时候,第一个人赢的概率

这个人可能这一轮就赢,即取到白老鼠dp[i][j] += i/(i+j);

也可能下一轮才赢,那么这一轮可能的情况是:

第一个人取到黑,第二个人取到黑,逃掉一只黑的 dp[i][j] += j/(i+j)*(j-1)/(i+j-1)*(j-2)/(i+j-2)*dp[i][j-3];

第一个人取到黑,第二个人取到黑,逃掉一只白的 dp[i][j] += j/(i+j)*(j-1)/(i+j-1)*i/(i+j-2)*dp[i-1][j-2];

还有一种可能是

第一个人取到黑,第二个人取到白, 但这不是我们要考虑的,我们要考虑的是第一个人赢的概率

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 typedef long long LL;                   
16 const int INF = 1<<30;
17 /*
18 dp[i][j] 表示有i只白鼠,j只黑鼠的时候,princess赢的概率
19 dp[i][j] = i/(i+j) + j/(i+j) * (j-1)/(i+j-1) * (j-2)/(i+j-2) * dp[i][j-3] + j/(i+j) * (j-1)/(i+j-1)*i/(i+j-2)*dp[i-1][j-2]
20 */
21 double dp[1000 + 10][1000 + 10];
22 int main()
23 {
24     int w, b, i, j;
25     scanf("%d%d", &w, &b);
26     for (i = 1; i <= w; ++i)
27         dp[i][0] = 1;//如果只有白,没有黑,那么第一次抓就会赢
28     for (i = 1; i <= w; ++i)
29     {
30         for (j = 1; j <= b; ++j)
31         {
32             dp[i][j] += (double)i / (i + j);
33             if (j >= 3)
34                 dp[i][j] += (double)j / (i + j) * (double)(j - 1) / (i + j - 1) * (double)(j - 2)/(i + j - 2)*dp[i][j - 3];
35             if (j >= 2)
36                 dp[i][j] += (double)j / (i + j) * (double)(j - 1) / (i + j - 1) * (double)i / (i + j - 2)*dp[i - 1][j - 2];
37         }
38         
39     }
40     printf("%.9lf\n", dp[w][b]);
41     return 0;
42 }
View Code

 

poj http://poj.org/problem?id=3744

给定n, p 表示有n个地雷,p表示人走一步的概率,1-p表示人跳两步的概率, 人的其实位置在1

接下来n个数字表示n个地雷的位置, (地雷的位置不是递增的,坑爹啊)

我们求人安全走出雷区的概率

状态转移方程很简单dp[1] = 1 ,  如果i不是雷区, dp[i] = dp[i-1] * p + dp[i-2] * (1-p),   如果i是雷区, dp[i]=0

但是雷区的长度实在是太长了,而这又是一个递推关系,所以我们可以用矩阵来优化

1--a[1]

a[1] + 1--a[2]

...进行分段

首先用矩阵快速幂求的dp[a[1]] ,dp[a[1]-1]

从而求的dp[a[1]+1] ,dp[a[1]],然后进行第二段矩阵快速幂,依次类推。

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <algorithm>
  5 #include <iostream>
  6 #include <queue>
  7 #include <stack>
  8 #include <vector>
  9 #include <map>
 10 #include <set>
 11 #include <string>
 12 #include <math.h>
 13 using namespace std;
 14 #pragma warning(disable:4996)
 15 typedef long long LL;                   
 16 const int INF = 1<<30;
 17 /*
 18 
 19 */
 20 class Matrix
 21 {
 22 public :
 23     double mat[2][2];
 24     void makeZero()
 25     {
 26         for (int i = 0; i < 2; ++i)
 27         for (int j = 0; j < 2; ++j)
 28             mat[i][j] = 0;
 29     }
 30     void makeUnit()
 31     {
 32         for (int i = 0; i < 2; ++i)
 33         for (int j = 0; j < 2; ++j)
 34             mat[i][j] = (i == j);
 35     }
 36 };
 37 Matrix operator*(const Matrix &lhs, const Matrix &rhs)
 38 {
 39     Matrix ret;
 40     ret.makeZero();
 41     for (int k = 0; k < 2; ++k)
 42     for (int i = 0; i < 2; ++i)
 43     {
 44         if (lhs.mat[i][k] == 0) continue;
 45         for (int j = 0; j < 2; ++j)
 46             ret.mat[i][j] += lhs.mat[i][k] * rhs.mat[k][j];
 47     }
 48     return ret;
 49 }
 50 Matrix operator^(Matrix a, int n)
 51 {
 52     Matrix ret;
 53     ret.makeUnit();
 54     while (n)
 55     {
 56         if (n & 1)
 57             ret = ret * a;
 58         n >>= 1;
 59         a = a * a;
 60     }
 61     return ret;
 62 }
 63 int a[10 + 10];
 64 int main()
 65 {
 66     int n, i, x;
 67     double p;
 68     int pre;
 69     Matrix aa;
 70     double f1, f2;
 71     while (scanf("%d%lf", &n, &p) != EOF)
 72     {
 73         bool flag = false;
 74         for (i = 1; i <= n; ++i)
 75         {
 76             scanf("%d", &a[i]);
 77             if (a[i] - a[i - 1] == 1)
 78                 flag = true;
 79         }
 80         sort(a + 1, a + n + 1);
 81         for (i = 1; i <= n; ++i)
 82         if (a[i] - a[i - 1] == 1)
 83             flag = true;
 84         if (flag)
 85             printf("%.7lf\n", 0);
 86         else
 87         {
 88             pre = 1;
 89             f1 = 0;
 90             f2 = 1;
 91             for (i = 1; i <= n; ++i)
 92             {
 93                 aa.mat[0][0] = p;
 94                 aa.mat[0][1] = 1;
 95                 aa.mat[1][0] = 1 - p;
 96                 aa.mat[1][1] = 0;
 97                 aa = aa ^ (a[i] - pre);
 98                 pre = a[i]+1;
 99                 
100                 //double tmpf2 = f2 * aa.mat[0][0] + f1*aa.mat[1][0];
101                 //double tmpf1 = f2 * aa.mat[0][1] + f1*aa.mat[1][1];
102                 //f2 = tmpf2;
103                 //f1 = tmpf1;
104                 //f2 = f1 * (1 - p);
105                 //f1 = 0;
106                 //这是根据上面的注释的代码推出来的
107                 f2 = f2 * aa.mat[0][1] * (1 - p);
108             }
109             printf("%.7f\n", f2);
110         }
111     }
112     return 0;    
113 }
View Code

 

uva10759 http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1700

给定n个骰子和数字x,要我们求掷n个骰子,点数至少为x的概率

dp[i][j] 为前i个骰子,点数为j的概率 dp[i][j] += dp[i-1][j-k]/6    j-k>=0,  初始条件dp[0][0] = 1;

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 typedef unsigned long long LL;                   
16 const int INF = 1<<30;
17 /*
18 
19 */
20 struct node//因为要输出分数,所以建立一个数据结构,存分子和分母
21 {
22     LL x, y;//x/y
23 };
24 node dp[30][200];
25 LL gcd(LL a, LL b)
26 {
27     if (b == 0)
28         return a;
29     return gcd(b, a%b);
30 }
31 int main()
32 {
33     int n, x;
34     while (scanf("%d%d", &n, &x), n || x)
35     {
36         memset(dp, 0, sizeof(dp));
37         dp[0][0].x = dp[0][0].y = 1;
38         int m = n * 6;
39         for (int i = 1; i <= n; ++i)
40         {
41             for (int j = i; j <= m; ++j)
42             {
43                 for (int k = 1; k <= 6; ++k)
44                 if (j - k >= 0 && dp[i - 1][j - k].y != 0)
45                 {
46                     if (dp[i][j].y == 0)
47                     {
48                         dp[i][j] = dp[i - 1][j - k];
49                         dp[i][j].y *= 6;
50                         continue;
51                     }
52                     LL y = dp[i - 1][j - k].y * 6;
53                     LL g = gcd(dp[i][j].y, y);
54                     LL tmp = dp[i][j].y / g * y;
55                     dp[i][j].x = dp[i][j].x * (tmp / dp[i][j].y) + dp[i - 1][j - k].x*(tmp / y);
56                     dp[i][j].y = tmp;
57                     //dp[i][j] += dp[i - 1][j - k] / 6;
58                 }
59             }
60         }
61         
62         node ans = dp[n][x];
63         for (int i = x + 1; i <= m; ++i)
64         {
65             if (dp[n][i].y == 0) continue;
66             if (ans.y == 0)
67             {
68                 ans = dp[n][i];
69                 continue;
70             }
71             LL g = gcd(ans.y, dp[n][i].y);
72             LL tmp = ans.y / g * dp[n][i].y;
73             ans.x = ans.x *(tmp / ans.y) + dp[n][i].x*(tmp / dp[n][i].y);
74             ans.y = tmp;
75         }
76         LL g = gcd(ans.x, ans.y);
77         if (g != 0)
78         {
79             ans.x /= g;
80             ans.y /= g;
81         }
82         if (ans.y != 0)
83         {
84             if (ans.x%ans.y == 0)
85                 printf("%llu\n", ans.x / ans.y);
86             else
87                 printf("%llu/%llu\n", ans.x, ans.y);
88         }
89         else
90             printf("0\n");
91     }
92     return 0;
93 }
View Code

 

下面的是求期望的

posted @ 2015-05-04 16:13  justPassBy  阅读(229)  评论(0编辑  收藏  举报