概率DP与期望DP不同,这个是正序的,接下来上一下我的思路,先上题:
Alice和Bob正在玩一个游戏:
一个袋子里一开始装着w个白球和b个黑球。Alice和Bob轮流随机抽出一个球(Alice先手)。如果抽出的球是白色的,则抽出这个球的人获胜。每当一个球被Bob取出后,会有另一个球滚出来(不算任何人抽的)。但Alice取出时很小心,不会让球滚出来。每个人抽球、和自动滚出来的球都是等概率的。那么Alice获胜率是多少呢?
如果最后袋子里没有球了,并且没有人拿到白球,那么Bob获胜。
Input
两个数w,b含义如上。w,b<=1000
Output
Alice获胜的概率,保留至少10位小数。绝对误差或相对误差不超过1e-9时被认为正确。
Sample Input
Input 1:
1 3
Input 2:
5 5
Sample Output
Output 1:
0.500000000
Output 2:
0.658730159
在第一个样例中,Alice第一次就取得白球并获胜的概率是1/4.。Bob第一次取到黑球的概率是3/4*2/3=1/2. 之后剩下一白一黑两个球; 一个滚出来,另一个被Alice在他的第二轮取到.如果Alice的球是白的,他获胜(1/2*1/2=1/4),否则没有人拿到白球,根据规则Bob获胜.
思路:
一开始开了个一维DP,硬是给弄不出来,后来想了想,DP有被称为状态转移方程,那么我枚举了一下Alice赢的时候的状态,不就是直接抓取白球,或者前两次她和Bob均抓了黑球(此间情况还会多出一种掉的球是黑是白的情况)——所以一共三种情况。那么一维DP想必是开不下了,于是我建了个二维的DP,dp[i][j],i表示白球的此时数量,j表示黑球的此时数量。
那么此时的DP方程该怎么去列写?知道概率DP是正序的,那么就由已知来往上推未知,所以有状态转移方程:
状态转移方程:
- 初始一次性就抓到白球dp[i][j]=(i/(i+j));
- 前两次都抓到了黑球,掉了白球:if(j>=2) dp[i][j]+=(j/(i+j))*((j-1)/(i+j-1))*(i/(i+j-2))*dp[i-1][j-2];
- 前两次都抓到了黑球,掉了黑球:if(j>=3) dp[i][j]+=(j/(i+j))*((j-1)/(i+j-1))*((j-2)/(i+j-2))*dp[i][j-3];
- 细节上,为了精度,我们令i、j转换成double类型。
完整代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
const int maxN=1005;
int W, B;
double dp[maxN][maxN]; //i个白球、j个黑球,且此时到了Alice拿球
int main()
{
while(scanf("%d%d", &W, &B)!=EOF)
{
memset(dp, 0, sizeof(dp));
dp[1][0]=1.;
for(int i=1; i<=W; i++)
{
for(int j=0; j<=B; j++)
{
double ii=i, jj=j;
dp[i][j]=ii/(ii+jj); //Alice抓到白球
if(j>=3) dp[i][j]+=(jj/(ii+jj)) * ((jj-1)/(ii+jj-1)) * ((jj-2)/(ii+jj-2)) * dp[i][j-3];
if(j>=2) dp[i][j]+=(jj/(ii+jj)) * ((jj-1)/(ii+jj-1)) * ((ii)/(ii+jj-2)) * dp[i-1][j-2];
}
}
printf("%.10lf\n", dp[W][B]);
}
return 0;
}