HDU-4405(飞行棋步数期望的 正推和逆推)

https://vjudge.net/problem/HDU-4405
题意:格子编号 0 − n 0-n 0n,从起点 0 0 0出发,扔骰子前进,当当前步数加上骰子上的数字 ≥ n ≥n n时游戏结束。同时有些格点直接相连,即若a,b相连,当落到a点时直接飞向b点,求至游戏结束时的步数期望。

先说逆推,我们可以用step[i]表示从i到n的期望步数,初始化step[n]=0。因为下一步的去向以及去向的概率很容易确定,所以:①如果直接相连,那么有step[a]=step[b];②否则,它可以先到达后面6格子中的一个,再到n,因为到后面每个格子的概率都是1/6,说以step[i] = ∑ k = l 6 =\sum\limits _{k=l}^6 =k=l6(step[i-k]+1) ∗ 1 6 *\frac{1}{6} 61

//倒推
#include
const int manx=1e5+10;
int n,m,net[manx];
double step[manx];//每个格子到n的步数的期望值
int main()
{
    int s,e;
    while(scanf("%d%d",&n,&m),n||m)
    {
        for(int i=0;i<=n+6;i++)net[i]=step[i]=0;
        while(m--)
        {
            scanf("%d%d",&s,&e);
            net[s]=e;
        }
        for(int i=n-1;i>=0;i--)
        {
            if(net[i])step[i]=step[net[i]];
            else step[i]=(step[i+1]+step[i+2]+step[i+3]+step[i+4]+step[i+5]+step[i+6])/6+1;
        }
        printf("%.4f\n",step[0]);
    }
    return 0;
}

关于正推:

首先,步数的期望 = ∑ ( =\sum( =(每一个可能的步数 × × ×以这个步数到达的概率 ) ) )

同样,我们可以用step[i]表示0到i的期望步数(即当我们到达i时 的期望步数),初始化step[0]=0。然后我们可以通过扔骰子从前面的几个格子到达i,或者从某个格子坐小飞机直接飞过来,但是每一种来源的概率不清楚。

所以我们用另一个数组p[i]来表示 能够到达i的概率(很明显p[0]=p[n]=1),初始化p[0]=1。现在考虑当我们达到i时,以每一种方式过来的概率:
在这里插入图片描述
①从点x坐小飞机直接飞到i:因为到达x的概率是p[x],且x只能飞向i,所以从点x过来的概率是p[x]/p[i];②从点i-k扔骰子过来,因为到达i-k的概率是p[i-k],且点i-k到i的概率是1/6,所以从点i-k过来的概率是(p[i-k] ∗ 1 6 *\frac{1}{6} 61)/p[i]

//顺推
#include
const int manx=1e5+10;
//顺推来源的概率不一样。而逆推去向的概率一样。
int n,m,net[manx];
double step[manx];//0到每个格子的步数的期望值
double p[manx];//0能到达某个格子的概率
int main()
{
    int s,e;
    while(scanf("%d%d",&n,&m),n||m)
    {
        for(int i=0;i<=n;i++)p[i]=net[i]=step[i]=0;
        while(m--)
        {
            scanf("%d%d",&s,&e);
            net[s]=e;
        }
        p[0]=1;
        for(int i=0;i<=n;i++)
        {
     	   //处理期望步数
            for(int j=max(0,i-6);j<=i-1;j++)
            {
                double f=1;
                if(i==n)f=7-n+j;//f是因为下一个格子大于等于n是都算游戏结束
                step[i]+=f*(step[j]+1)*p[j]/6;
            }
            if(p[i])
            step[i]/=p[i];
            //处理概率,同时处理可以做小飞机的地方
            if(net[i])
            {
                p[net[i]]+=p[i];
                step[net[i]]+=p[i]*step[i];
                p[i]=0;//这里赋为0后面就不用判断是否能从这一点摇子过去了
            }
            else
            {
                for(int j=i+1;j<=i+6;j++)
                {
                    int temp=min(n,j);
                    p[temp]+=p[i]/6;
                }
            }
        }
        printf("%.4f\n",step[n]);
    }
    return 0;
}
/*
10 6
2 5
3 5
4 5
5 7
7 9
6 9
*/

但是因为没注意除p[i]的时候p[i]可能等于0,百度也没有找到正推的写法,然后过几天写次过几天写次还是一直没发现问题。
在这里插入图片描述
另外这里有没有相连点时顺推和逆推的写法:用投色子问题分析为什么顺着推、期望反着推 / 概率论

你可能感兴趣的:(概率期望)