hdu4405:概率dp

题意:

总共有n+1个格子:0-n

初始情况下在 0号格子 每次通过掷骰子确定前进的格子数

此外 还有一些传送门可以瞬间从 u 点传送到 v 点(必须被传送)

求走到(或超过)n点总共需要掷多少次骰子

分析:

太弱 只想到了n^2的 dp方程 可惜n是100000...纠结半天又看了大牛的题解

用 dp[i]记录 走到第 i 个点时的期望 p[i]记录第 i 个点的概率。。、

这个概率记录的感觉比较神奇 ,我先开始想到的n^2是记录用 i 步 走到 j 点的概率

题解的这个概率应该是??平均后的???f反正就是走到这个点了的概率直接把步数省去了 dp方程就少了一维 

dp[i]=次数 * p 那么 如果对于 i+j 点 dp[i+j]= (次数+1)*p*1/6 = ( dp[i]+p[i] ) / 6;

就可以写出转移方程了

同时在维护一个next数组记录 传送门 如果next[i]!=i 则证明不可能停在第 i 点 因此就不通过此点进行转移 直接continue,并把 i 的信息保存在next[i]中

统计答案的时候注意要统计 n到 n+5的和

ac代码:

#include <iostream>
#include <stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<ctype.h>
using namespace std;
#define MAXN 10000
int n,m;
int next[100010];
double dp[100010];
double p[100010];
void ini()
{
    memset(dp,0,sizeof(dp));
    memset(p,0,sizeof(p));
    for(int i=0;i<=100000;i++)
    {
        next[i]=i;
    }
    int u,v;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&u,&v);
        next[u]=v;
    }
}
void solve()
{
    dp[0]=0;
    p[0]=1;
    for(int i=0;i<n;i++)
    {
        if(next[i]!=i)
        {
            p[next[i]]+=p[i];
            dp[next[i]]+=dp[i];
            p[i]=0;
            continue;
        }
        for(int j=1;j<=6;j++)
        {
            p[i+j]+=p[i]*1.0/6.0;
            dp[i+j]+=(dp[i]+p[i])*1.0/6.0;
        }
    }
    double ans=0;
    for(int i=n;i<n+6;i++)
    {
        ans+=dp[i];
    }
    printf("%.4f\n",ans);
}
int main()
{
    while(scanf("%d%d",&n,&m),m+n)
    {
        ini();
        solve();
    }
    return 0;
}

 

posted @ 2014-09-29 22:05  PlasticSpirit  阅读(146)  评论(0编辑  收藏  举报