首页 > 代码库 > ZOJ 3329 期望DP
ZOJ 3329 期望DP
题目大意:
给定3个已经规定好k1,k2,k3面的3个色子,如果扔到a,b,c则重新开始从1 计数,否则不断叠加所有面的数字之和,直到超过n,输出丢的次数的数学期望
我们在此令dp[]数组记录从当前数值到结束的数学期望
假如有3个面数都为2的色子
那么dp[i] = 1.0 / 2/2/2 * dp[0] + 1.0/8*dp[i+3] +3.0/8*dp[i+4]+3.0/8*dp[i+5]+1.0/8*dp[i+6] + 1
当然那些下标大于i的dp值均为0
可是我们这样从后往前推会导致无法计算dp[0]的数值,没法推
从新寻找规律,可以看做
dp[i] = a[i] * dp[0] + b[i]; 1式
dp[i] = p0 * dp[0] + ∑(dp[i+k]*p[k]) + 1; 2式
1式代人2式
dp[i] = p0*dp[0] + ∑((a[i+k]*dp[0]+b[i+k]))*p[k])+1
dp[i] =( p0+∑(a[i+k]*p[k])) dp[0] + ∑(b[i+k]*p[k]) +1
所以a[i] =p0+∑(a[i+k]*p[k]) b[i] = ∑(b[i+k]*p[k]) +1
这样我们由后往前推不断得到所有的a[i]和b[i]值
dp[0] = a[0]*dp[0]+b[0]
这样我们得到a[0],b[0]就很容易得到dp[0]的值了
这是这段叠加处求a,b数组的代码
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=n;i>=0;i--){
for(int j=3;j<=maxn;j++)
{
a[i]+=a[i+j] * pro[j];
b[i]+=b[i+j] * pro[j];
}
a[i]+=1.0/k1/k2/k3;
b[i]+=1;
}
1 #include <cstdio> 2 #include <cstring> 3 4 using namespace std; 5 double pro[20],a[600],b[600]; 6 7 int main() 8 { 9 int n,k1,k2,k3,d,e,f,T;10 scanf("%d",&T);11 while(T--){12 scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&d,&e,&f);13 14 int maxn = k1+k2+k3;15 memset(pro,0,sizeof(pro));16 for(int i=1;i<=k1;i++){17 for(int j=1;j<=k2;j++){18 for(int k=1;k<=k3;k++)19 if(i!=d||j!=e||k!=f)20 pro[i+j+k]++;21 }22 }23 24 for(int i=3;i<=maxn;i++)25 pro[i] = pro[i]*1.0/k1/k2/k3;26 27 memset(a,0,sizeof(a));28 memset(b,0,sizeof(b));29 for(int i=n;i>=0;i--){30 for(int j=3;j<=maxn;j++)31 {32 a[i]+=a[i+j] * pro[j];33 b[i]+=b[i+j] * pro[j];34 }35 a[i]+=1.0/k1/k2/k3;36 b[i]+=1;37 }38 39 double ans = b[0] / (1-a[0]);40 printf("%.10f\n",ans);41 }42 }
ZOJ 3329 期望DP