期望dp专题
一直不明白为什么概率是正推,期望是逆推。 现在题目做多了,慢慢好像有点明白了
poj2096
收集bug, 有n个种类的bug,和s个子系统。 每找到一个bug需要一天。
要我我们求找到n个种类的bug,且在每个系统中都找到一个bug的期望天数
设dp[i][j] 为找到i个种类的bug和在j个系统中找到bug后,还需要的期望天数
那么dp[n][s] 肯定是0,而dp[0][0]是我们要求的。 这也就是为什么期望是要逆推。
还有一点就是这一状态的期望会等于 所有(下一状态的的期望*这一状态走向下一状态的概率)的和+1
所有可能的情况如下
找到了一个新种类的bug (n-i)/n 第一维度增加1,在一个已经找到bug的系统里面j/s, 第二维度不增加
找到了一个旧种类的bug i/n 第一维度不增加,在一个没有找到bug的系统里面 (s-j)/s 第二维度增加,
找到了一个新种类的bug (n-i)/n 第一维度增加,在一个没有找到bug的系统里面 (s-j)/s 第二维度增加,
找到了一个旧种类的bug i/n 第一维度不增加 , 在一个已经找到bug的系统里面 j/s 第二维度不增加
所有状态转移方程是
dp[i][j] = (1-i/n)*j/s*dp[i+1][j] + i/n*(1-j/s)*dp[i][j+1] + (1-i/n)*(1-j/s) * dp[i+1][j+1] + i/n*j/s*dp[i][j] + 1
将红色的部分移到左边化简后得到
dp[i][j] = ((1-i/n)*j/s*dp[i+1][j] + i/n*(1-j/s)*dp[i][j+1] + (1-i/n)*(1-j/s) * dp[i+1][j+1] +1)/(1-i/n*j/s)
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 #pragma comment(linker, "/STACK:1024000000,1024000000") 16 typedef long long LL; 17 const int INF = 1 << 30; 18 /* 19 double dp[n][s] 表示已经找到n个种类的bug,在s个子系统中都找到了bug的期望天数 20 21 找到了一个新种类的bug (n-i)/n 第一维度增加1,在一个已经找到bug的系统里面j/s, 第二维度不增加 22 找到了一个旧种类的bug i/n 第一维度不增加,在一个没有找到bug的系统里面 (s-j)/s 第二维度增加, 23 找到了一个新种类的bug (n-i)/n 第一维度增加,在一个没有找到bug的系统里面 (s-j)/s 第二维度增加, 24 找到了一个旧种类的bug i/n 第一维度不增加 , 在一个已经找到bug的系统里面 j/s 第二维度不增加 25 26 dp[i][j] = (1-i/n)*j/s*dp[i+1][j] + i/n*(1-j/s)*dp[i][j+1] + (1-i/n)*(1-j/s) * dp[i+1][j+1] + i/n*j/s*dp[i][j] 27 28 dp[i][j] = ((1-i/n)*j/s*dp[i+1][j] + i/n*(1-j/s)*dp[i][j+1] + (1-i/n)*(1-j/s) * dp[i+1][j+1] +1)/(1-i/n*j/s) 29 30 */ 31 double dp[1000 + 10][1000 + 10]; 32 int main() 33 { 34 35 int n, s; 36 while (scanf("%d%d", &n, &s) != EOF) 37 { 38 memset(dp, 0, sizeof(dp)); 39 dp[n][s] = 0; 40 for (int i = n; i >= 0; --i) 41 { 42 for (int j = s; j >= 0; --j) 43 { 44 if (i == n &&j == s)continue; 45 dp[i][j] += (1 - (double)i / n)*j / s*dp[i + 1][j] + (double)i / n*(1 - (double)j / s)*dp[i][j + 1] + (1 - (double)i / n)*(1 - (double)j / s)*dp[i + 1][j + 1] + 1; 46 dp[i][j] /= (1 - (double)i / n*j / s); 47 } 48 } 49 printf("%.4lf\n", dp[0][0]); 50 } 51 return 0; 52 }
hdu4405
给我们n+1个格子, 标号0到n,一个人初始在位置0,然后丢骰子向前走,
然后又给定m个 a b , 表示到了位置a,能够到飞到位置b而不需要丢骰子
问走到>=n的位置 需要丢骰子的期望次数
设dp[i] 为从位置i到>=n的位置需要丢骰子的期望次数
dp[i>=n] = 0
dp[i] = 1/6 * dp[i+1] + 1/6*dp[i+2] + ... + 1/6 * dp[i+6]
如果位置i+k 能够飞行, 那么应该用飞行过后的位置的期望带入去算
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 #pragma comment(linker, "/STACK:1024000000,1024000000") 16 typedef long long LL; 17 const int INF = 1<<30; 18 /* 19 dp[i>=n] = 0; 20 dp[i] = dp[i+k] * 1/6 21 */ 22 double dp[100000 + 10]; 23 int hs[100000 + 10]; 24 int main() 25 { 26 int n, m; 27 int x, y; 28 while (scanf("%d%d", &n, &m), n) 29 { 30 memset(dp, 0, sizeof(dp)); 31 memset(hs, 0, sizeof(hs)); 32 for (int i = 0; i < m; ++i) 33 { 34 scanf("%d%d", &x, &y); 35 hs[x] = y; 36 } 37 for (int i = n-1; i >= 0; --i) 38 { 39 dp[i] = 1; 40 for (int k = 1; k <= 6; ++k) 41 { 42 if (hs[i + k] != 0) 43 { 44 int t = i + k; 45 while (hs[t] != 0)//找到飞行的最终位置, 因为能够多次飞行 46 { 47 t = hs[t]; 48 } 49 dp[i] += dp[t] / 6; 50 } 51 else 52 dp[i] += dp[i + k] / 6; 53 } 54 } 55 printf("%.4lf\n", dp[0]); 56 } 57 return 0; 58 }
hdu3853
一个人在迷宫的位置(1,1) 要逃到 迷宫的位置(n,m)
在位置(i,j) 需要2魔力去开启传送阵, 有p1的概率是留在原地, p2的概率往右走,p3的概率往下走
问我们逃到迷宫的位置(n,m)需要的魔力的期望
dp[i][j] = p1 * dp[i][j] + p2*dp[i][j+1] + p3*dp[i+1][j] + 2
dp[i][j] = ( p2*dp[i][j+1] + p3*dp[i+1][j] + 2 ) / (1-p1)
还有一个坑是如果某点停留在原地的概率为1, 这是允许的, 那么肯定所有的点都不会到达这一点,否则答案就会很大,不符合题目所说的
所以应该讲该点的dp[i][j]置为0, 而不应该去计算(万一计算出来很大呢)不然会影响之后的值, 因为虽然走到该点的概率是0, 但是浮点数毕竟有误差。
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 #pragma comment(linker, "/STACK:1024000000,1024000000") 16 typedef long long LL; 17 const int INF = 1<<30; 18 /* 19 dp[i][j] = 2 + dp[i][j+1] * p[i][j][1] + dp[i+1][j] * p[i][j][2] + dp[i][j] * p[i][j][0] 20 dp[i][j] = (2 + dp[i][j+1]*p[i][j][1] + dp[i+1][j] * p[i][j][2])/(1-P[i][j][0]); 21 */ 22 const int N = 1000 + 10; 23 double p[N][N][3]; 24 double dp[N][N]; 25 int main() 26 { 27 int n, m; 28 while (scanf("%d%d", &n, &m) != EOF) 29 { 30 for (int i = 1; i <= n; ++i) 31 for (int j = 1; j <= m; ++j) 32 for (int k = 0; k < 3; ++k) 33 scanf("%lf", &p[i][j][k]); 34 dp[n][m] = 0; 35 for (int i = n; i >= 1; --i) 36 { 37 for (int j = m; j >= 1; --j) 38 { 39 if (i == n &&j == m) 40 continue; 41 if (fabs(1 - p[i][j][0]) <= 1e-5)//如果该点停留在原地的概率为1, 这是允许的, 那么肯定所有的点都不会到达这一点,否则答案就会很大,不符合题目所说的 42 { 43 dp[i][j] = 0; 44 continue; 45 } 46 dp[i][j] = (p[i][j][1] * dp[i][j + 1] + p[i][j][2] * dp[i + 1][j]) / (1 - p[i][j][0]) + 2/(1-p[i][j][0]); 47 } 48 } 49 printf("%.3lf\n", dp[1][1]); 50 } 51 return 0; 52 }