设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);
}


京公网安备 11010502036488号