概率DP初步

题目简述:
一个r行c列的格子,起始点在(1,1),终点在(r,c),每一步可能的走法有:不动、向右走、向下走,每走一步花费两点魔法值,现给出在每一点三种走法的概率,求走完迷宫时所花魔法值的期望。
分析:
运用DP算法的话,首先需要确定一个合适状态来描述子问题的情况,很明显本题的状态可以定义为dp[i][j],代表从(i,j)到(r,c)所花费魔法值。然后我们需要考虑这样的状态之间能否正确的转化,利用数学期望的定义以及其线性,不难写出如下转移方程:

dp[i][j] = p[i][j][1]*dp[i][j] + p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2

,然后再合并同类项可得:

dp[i][j] = (p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2)/(1-p[i][j][1])。

最后,需要确定边界,很明显,dp[r][c]=0,因为当在点(r,c)时,他不需要花费魔法值就可以到达(r,c),这样我们就可以从后往前递推了。
附上代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1005+10;
double p[maxn][maxn][3],dp[maxn][maxn];
int main(){
	int r,c;
	while(~scanf("%d%d",&r,&c)){ 
	    memset(dp,0,sizeof(dp));
	    memset(p,0,sizeof(p));
	    for(int i=1;i<=r;i++)
	        for(int j=1;j<=c;j++)
	            for(int k=1;k<=3;k++)  
	                scanf("%lf",&p[i][j][k]);
	    dp[r][c]=0;
		for(int i=r;i>=1;i--)
	    	for(int j=c;j>=1;j--){
	        	if((i==r&&j==c)||p[i][j][1]==1)
	           		continue;
	        	dp[i][j]=(p[i][j][2]*dp[i][j+1]+p[i][j][3]*dp[i+1][j]+2)/(1-p[i][j][1]);
    		}
    	printf("%.3lf\n",dp[1][1]);
    }
    return 0;
}

题意简述:
有0-n个格子,初始点在0,终点是>=n,每走一步之前都要丢一次六个面的色子,标上1-6,扔到几就走几步,当然色子是等概率出现数字的,还有就是中间某一点可能和其它的一点联通,比如a和b联通,当我处于a时,就可以直接飞到b,最后问走到终点时所扔色子次数的期望。

分析:
显然,首先从后往前推,最后一个肯定是0,然后,如果有格子与这次的第i格相连,就可以开始跳跃,不然,就加上dp[1——6]*1.0/6.0,最后再加上1即可,再输出dp[0]即可轻松AC掉。
所以,附上代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=100000+19;
double dp[maxn];
int a[maxn];
int main(){
	int n,m;
	while(~scanf("%d%d",&n,&m)){
		if(n+m==0)
		   return 0;
		memset(dp,0,sizeof(dp));
		memset(a,0,sizeof(a));
		for(int i=1;i<=m;i++){
			int x,y;
			cin>>x>>y;
			a[x]=y;
		}
		for(int i=n-1;i>=0;i--){
			if(a[i]){
				dp[i]=dp[a[i]];
				continue;
			}
			dp[i]=(dp[i+1]+dp[i+2]+dp[i+3]+dp[i+4]+dp[i+5]+dp[i+6])/6+1;
		}
		printf("%.4lf\n",dp[0]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42875611/article/details/82902915