HDOJ 4405 Aeroplane chess(概率入门)

题意:飞行棋,起点是 0 ,终点是 n 和 n 以后的点,然后会给出 m 条边,表示直接从 x 飞到 y,而且是必须飞,不需要扔骰子,然后就是飞行棋的规则,扔一次骰子,按照骰子的步数走多少步 ,求出从起点到终点的扔骰子的期望.

最近算是第一次做概率DP吧,学一学这个东西。
以 dp[i] 表示从 i 点到终点的期望次数,那么 dp[n] 到 dp[n+6] 就肯定是 0 了,这个就不用解释了.
由于求期望,逆推。那么对于 i 点,可以走到 i+1,i+2,一直到 i+6,概率都是 六分之一,那么就可以得出: dp[i] = (dp[i+1]+.....+dp[i+6])/6+1;
这个转移依靠的是期望的线性关系。这是在没有边的情况下的转移,但是由于有边,再来考虑有边的情况,对于边连接的两个点(x,y),有: dp[x] = dp[y] 这个性质,
因为在飞行棋中,x,y,是连接在一起的,其实可以看做是同一个点,那么对于这两个点的 dp 值,应该是相等的,这个要仔细推敲一下就能知道。
我的代码写得就比较复杂,用并查集维护了联通块,对于块内的点dp值肯定相同,其实可以写得很简单,我果然代码能力还是太弱。

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
int f[maxn],n,m,vis[maxn],flag[maxn],cnt[maxn];
double dp[maxn],p[maxn];
void inti()
{
    memset(cnt,0,sizeof(cnt));
    memset(flag,-1,sizeof(flag));
    memset(dp,0,sizeof(dp));
    for(int i=0;i<=n;i++)
        f[i]=i;
}
int findfa(int x)
{
    if(x==f[x])
        return x;
    return f[x]=findfa(f[x]);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        inti();
        if(n==0&&m==0)
            break;
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            int dx=findfa(x);
            int dy=findfa(y);
            if(dx!=dy)
                f[dx]=y;
        }
        for(int i=0;i<=n;i++)
        {
            int di=findfa(i);
            vis[i]=di;
            cnt[di]++;
        }

        for(int i=n;i<=n+7;i++)
            dp[i]=0.0;
        int di=findfa(n);
        flag[di]=1;
        p[di]=0.0;
        for(int i=n-1;i>=0;i--)
        {
            int di=vis[i];
            if(cnt[di]==1)
            {
                double tmp=0.0;
                for(int j=1;j<=6;j++)
                    tmp+=dp[i+j];
                dp[i]=tmp/6.0+1;
            }
            else
            {
                if(flag[di]==-1)
                {
                    double tmp=0.0;
                    for(int j=1;j<=6;j++)
                        tmp+=dp[i+j];
                    dp[i]=tmp/6.0+1;
                    p[di]=dp[i];
                    flag[di]=1;
                }
                else
                    dp[i]=p[di];
            }
        }
        printf("%.4f\n",dp[0]);
    }
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值