首先,概率dp主要解决的是关于概率问题和期望问题的求解。
难点和普通dp一样在于dp[i][j][k]的维数控制和含义,其实就是转移方程的构建。
然后一般地,求概率是正推、求期望是逆推。(开始的很多状态不可能发生概率为0,最后的状态出口期望为0)
对于求概率当前点的概率是由前面能到达当前点的点乘上到达当前点的概率得到的。
也就是dp[i]=Σ(dp[j]*p[j][i]) i是当前点、j是前面的点。
对于求期望当前点的期望是由当前点所能到达的点得到的。(注意下,对应的概率是当前点到达之后点的概率,因为你要到达后面的点后面的点才能传递给你期望!)
也就是E[i]=Σ((E[j]+k)*p[i][j]) i是当前点、j是当前点所能到达的点,k是所需的期望值。
然后就是对于期望的话,如果成环的话数据范围小的话可以用高斯消元解决(之前的博文提到),如果范围大就要推导公式了(后面的题目有提到)。
poj 2096 Collecint Bugs
dp求期望入门题。
题意,思路,公式引用别人的:http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710606.html
dp求期望
逆着递推求解
题意:(题意看题目确实比较难道,n和s都要找半天才能找到)
一个软件有s个子系统,会产生n种bug
某人一天发现一个bug,这个bug属于一个子系统,属于一个分类
每个bug属于某个子系统的概率是1/s,属于某种分类的概率是1/n
问发现n种bug,每个子系统都发现bug的天数的期望。
求解:
dp[i][j]表示已经找到i种bug,j个系统的bug,达到目标状态的天数的期望
dp[n][s]=0;要求的答案是dp[0][0];
dp[i][j]可以转化成以下四种状态:
dp[i][j],发现一个bug属于已经有的i个分类和j个系统。概率为(i/n)*(j/s);
dp[i][j+1],发现一个bug属于已有的分类,不属于已有的系统.概率为 (i/n)*(1-j/s);
dp[i+1][j],发现一个bug属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(j/s);
dp[i+1][j+1],发现一个bug不属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(1-j/s);
整理便得到转移方程
代码:
#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define eps 1e-5
using namespace std;
double dp[1020][1020];
int main()
{
int n,s;
while(cin>>n>>s)
{
memset(dp,0,sizeof(dp));
for(int i=n; i>=0; i--)
{
for(int j=s; j>=0; j--)
{
if(i==n&&j==s) continue;
double tep=1.0;
if(i<n) tep+=(n*1.0-i)*j/(n*s)*dp[i+1][j];
if(j<s) tep+=(s*1.0-j)*i/(n*s)*dp[i][j+1];
if(i<n&&j<s) tep+=(n*1.0-i)*(s*1.0-j)/(n*s)*dp[i+1][j+1];
dp[i][j]=tep/(1-(i*j*1.0)/(s*n));
}
}
printf("%.4f\n",dp[0][0]);
}
return 0;
}
类似的简单题:
hdu 3853 注意停留原地的情况,应该跳过不处理。还有一点走一步的期望是2。
hdu 4405 处理一下连续移动的情况
SGU 495 用概率求期望
CF 148D 考虑下边界就OK了,方程很好推
zoj 3640 Help Me Escape
题意:
一只吸血鬼,有n条路给他走,每次他随机走一条路,
每条路有个限制,如果当时这个吸血鬼的攻击力大于等于某个值,那么就会花费t天逃出去。
否则,花费1天的时间,并且攻击力增加,问他逃出去的期望天数。
思路:
方程很好像 dp[i] 有i点战斗力逃出去的期望
那么如果 x>c[i]时 dp[i]=(1.0/n)*Σt[i]
否则 dp[i]=(1.0/n)*Σ(dp[i+c[i]]+1)
因为很多点会被重复用,所以写成记忆化搜索。
还有一个要注意的,t[i]是向下取整的。
代码:
#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cma