zoj3329(概率dp)

时间:2023-03-09 05:36:37
zoj3329(概率dp)

题目连接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3754

题意:有三个骰子,分别有k1,k2,k3个面。 每次掷骰子,如果三个面分别为a,b,c则分数置0,否则加上三个骰子的分数之和。 当分数大于n时结束。求游戏结束时的期望步数。

分析:这题状态转移方程挺容易想,但递推化简时又是困难重重,还是得多练习。

设dp[i]表示在i分时到达目标状态的期望,pk为投掷k分的概率,p0为回到0的概率

则dp[i]=∑(pk*dp[i+k])+dp[0]*p0+1; 都和dp[0]有关系,而且dp[0]就是我们所求,为常数

设dp[i]=A[i]*dp[0]+B[i];

代入上述方程右边得到: dp[i]=∑(pk*A[i+k]*dp[0]+pk*B[i+k])+dp[0]*p0+1 =(∑(pk*A[i+k])+p0)dp[0]+∑(pk*B[i+k])+1;

明显 A[i]=(∑(pk*A[i+k])+p0)   B[i]=∑(pk*B[i+k])+1

先递推求得A[0]和B[0]. 那么 dp[0]=B[0]/(1-A[0]);

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdlib>
#include <stack>
#include <vector>
#include <set>
#include <map>
#define LL long long
#define mod 100000000
#define inf 0x3f3f3f3f
#define eps 1e-9
#define N 100010
#define FILL(a,b) (memset(a,b,sizeof(a)))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
double A[],B[],p[];
int main()
{
int T,n;
int k1,k2,k3,a,b,c;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
double p0=1.0/k1/k2/k3;
FILL(p,);
for(int i=;i<=k1;i++)
for(int j=;j<=k2;j++)
for(int l=;l<=k3;l++)
if(i!=a||j!=b||l!=c)
p[i+j+l]+=p0;
FILL(A,);FILL(B,);
for(int i=n;i>=;i--)
{
A[i]=p0;B[i]=;
for(int j=;j<=k1+k2+k3;j++)
{
A[i]+=A[i+j]*p[j];
B[i]+=B[i+j]*p[j];
}
}
printf("%.15lf\n",B[]/(-A[]));
}
}