设f[i]表示达到i分时到达目标状态的期望,pk为投掷k分的概率,p0为回到0的概率,这个先
预处理出来
请在这里输入引用内容
则f[i]=∑(pkf[i+k])+f[0]p0+1
请在这里输入引用内容
• f[i]=∑(pkf[i+k])+f[0]p0+1
• 每个状态都和f[0]有关系,而且f[0]就是我们所求,为一个常数
• 设f[i]=A[i]f[0]+B[i];
• 代入上述方程右边得到:
• f[i]=∑(pkA[i+k]f[0]+pkB[i+k])+f[0]p0+1=
• (∑(pkA[i+k])+p0)f[0]+∑(pkB[i+k])+1;
• 所以 A[i]=(∑(pkA[i+k])+p0) B[i]=∑(pk*B[i+k])+1
• 先递推求得A[0]和B[0] 那么 f[0]=B[0]/(1-A[0]);
以上是雨巨上课讲的思路
代码实现有一些小细节
首先,p数组统计概率,直接枚举三层循环,统计一下骰子各点的概率,特别地p0=1/k1/k2/k3
由于要求的是大于等于n的,所以从n开始递推,意义是n点需要投骰子的次数的数学期望,依次向前递推。
每次都要向上找k1+k2+k3个,不然会漏统计。
(数组大小要开足够,不然会WA,不会报段错误)
#include <bits/stdc++.h> using namespace std; double p[20];//骰子有三个,所以0、1、2都不可能 double A[550],B[550]; int main() { int n,k1,k2,k3,a,b,c; scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c); p[0]=1.0/k1/k2/k3;//三个骰子都是1 for(int i=1;i<=k1+k2+k3;i++) { int cnt=0; for(int j=k1;j>=1;j--)//第一个骰子 { for(int k=k2;k>=1;k--)//第二个骰子 { for(int l=k3;l>=1;l--)//第三个骰子 { if(j==a&&k==b&&l==c) { //如果会跳到0,不统计 } else if(j+k+l==i)//三个骰子之和等于i { cnt++; } } } } p[i]=1.0*cnt/k1/k2/k3; } for(int i=n;i>=0;i--)//递推 { for(int k=3;k<=k1+k2+k3;k++)//枚举骰子点数总和 { A[i]+=p[k]*A[i+k]; B[i]+=p[k]*B[i+k]; } A[i]+=p[0]; B[i]+=1.0; } double ans=B[0]/(1-A[0]); printf("%.10lf\n",ans); }
n4循环并不是必须的,在雨巨的指点下,将n4循环改为了n3循环
#include <bits/stdc++.h> using namespace std; double p[20];//骰子有三个,所以0、1、2都不可能 double A[550],B[550]; int main() { int n,k1,k2,k3,a,b,c; scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c); p[0]=1.0/k1/k2/k3;//三个骰子都是1 for(int i=1;i<=k1+k2+k3;i++) { int cnt=0; for(int j=k1;j>=1;j--)//第一个骰子 { for(int k=k2;k>=1;k--)//第二个骰子 { int l=i-j-k; if(l<1||l>k3) continue; if(j==a&&k==b&&l==c) { //如果会跳到0,不统计 } else if(j+k+l==i)//三个骰子之和等于i { cnt++; } } } p[i]=1.0*cnt/k1/k2/k3; } for(int i=n;i>=0;i--)//递推 { for(int k=3;k<=k1+k2+k3;k++)//枚举骰子点数总和 { A[i]+=p[k]*A[i+k]; B[i]+=p[k]*B[i+k]; } A[i]+=p[0]; B[i]+=1.0; } double ans=B[0]/(1-A[0]); printf("%.10lf\n",ans); }