HDU4405(期望DP)
又一道期望DP,其实这题与hdu4576那道概率DP很像(这道我也写了题解)。那么这两道一道求概率,一道求期望,又能放在一起对比一下了,期望和概率的求法的不同。
先总结一句话:一般求概率DP或者是递推的问题,都是正着推,从初始状态往结束状态(结束状态一般是此类题要求的状态)推,所以先得到(或者说先已知)的是靠近初始状态的状态,所以想要求的当前状态是由可转移到此状态的前N可能个状态推过来的;而一般求期望DP,都是逆着推,从结束状态往初始状态(初始状态往往是此类题要求的状态)推,所以先得到(或者说先已知)的是靠近结束状态的状态,所以想要求的当前状态是由此状态对应接下来的N可能个状态推过来的。
本题题意:数轴上有N+1个点(编号0~N),一个人玩游戏,从0出发,当到达N或大于N的点则游戏结束。每次行动掷骰子一次,骰子编号1-6,掷到多少就向前走几步,这个数轴上还有些特殊点,这些点类似飞行棋中的飞行点,只要到达这些点就可以直接飞到给定点。求总共投掷骰子次数的期望。
例如本题,倒着过来分析。用dp[i]表示在i位置时,距离游戏结束还要投掷次数的期望。显然dp[n]为0,需要求的是dp[0]。对于直接飞过去的点。例如用数组vis[]来表示,vis[a]=b,表示当到达a点时可以直接飞到b点,那么显然dp[vis[a]]=dp[a]。倒着推,dp[i](假设该点不属于可飞行的点)的下面一个状态有6种可能(即对应6种可能的骰子数),每种都是1/6的概率。所以for(int x=1;x<=6;x++)dp[i]+=dp[i+x]/6.0;dp[i]+=1;注意最后加玩每种可能性的期望后要+1,因为这6种可能性加起来只是下一个状态的期望,当前状态是他们的前一个状态,所以期望(直接理解为投掷骰子的次数)要+1。具体代码如下:
#include <stdio.h> #include <string.h> #include <algorithm> #include <cstring> #define ll long long double dp[100005]; int vis[100005]; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF){ if((n+m)==0)break; memset(vis,-1,sizeof(vis)); for(int i=1;i<=m;i++){ int a,b; scanf("%d%d",&a,&b); vis[a]=b; } memset(dp,0,sizeof(dp)); for(int i=n-1;i>=0;i--){ if(vis[i]==-1){ for(int j=1;j<=6;j++){ dp[i]+=dp[i+j]/6.0; } dp[i]+=1; } else dp[i]=dp[vis[i]]; } printf("%.4lf\n",dp[0]); } return 0; }