设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]=∑(pk
A[i+k]f[0]+pkB[i+k])+f[0]p0+1=
• (∑(pk
A[i+k])+p0)f[0]+∑(pkB[i+k])+1;
• 所以 A[i]=(∑(pk
A[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);
}