概率dp入门的简单题(hdu 4405,牡丹江,codeforces)

最近学习了一下概率dp,感觉没有想象中的那么难。主要还是状态的转移方程的构建。

对于”求概率正推,求期望反推“这句话有了初步的理解。期望的意思是说你现在处在一个状态,还需要几步到达最终状态,这是一个期望值,

假如我现在已经在目标状态,那么期望是0,即解决了初始化的问题,否则并不知道起点的期望值。

附上几道入门题:
hdu 4405  http://acm.hdu.edu.cn/showproblem.php?pid=4405

题意:飞行棋,每次掷骰子,向前移动的距离是当前位置+骰子的点数(1~6),还有一些直达点,求由0到n的掷骰子次数的期望。

逆推。从终点逆推回来,状态的转移看文中代码把。

//hdu 4405

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
int _hash[100010];
double dp[100010];
int main()
{
	int n,m,x,y;
	while(cin>>n>>m)
	{
		if(n==0&&m==0)
		break;
		memset(_hash,-1,sizeof(_hash));
		for(int i=1;i<=m;i++)
		{
			cin>>x>>y;
			_hash[x]=y;
		}
		memset(dp,0,sizeof(dp));//初始值dp[n]=0 
		for(int i=n-1;i>=0;i--)
		{
			if(_hash[i]!=-1)
			dp[i]=dp[_hash[i]];//走捷径 
			else
			{
			   for(int j=1;j<=6;j++)
			   dp[i]+=1.0/6*(dp[i+j]+1);//到达i的状态是由(i+1....i+6)来的	
			}
			
		}
		cout<<setiosflags(ios::fixed)<<setprecision(4)<<dp[0]<<endl;
	}
	return 0;
	
}
牡丹江现场赛的一道棋盘问题: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=77292#problem/C

题意:n*m的棋盘,每天放置一枚棋子,求每行每列都至少有一颗棋子的期望天数。

概率dp的裸题,但是要注意细节的处理。我们设dp[i][j][k]是第k天可以控制i行j列的概率,则状态的转移,k+1天要么还是控制i行j列,要么是i+1,j或者i,j+1,或者i+1,j+1

最后的最大天数一定是n*m-min(n,m)+1。

//牡丹江棋盘问题 

#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<cmath>
using namespace std;
double dp[55][55][55*55];
int T,n,m;
double ans;
int main()
{
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		memset(dp,0,sizeof(dp));
		int pmax=n*m-min(n,m)+1;//最终需要的最大天数 
		dp[1][1][1]=1.0;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				for(int k=max(i,j);k<=i*j;k++)
				{
					if(i==n&&j==m)//及时跳出 
					break;
					dp[i][j][k+1]+=dp[i][j][k]*(i*j-k)/(n*m-k);
					dp[i+1][j][k+1]+=dp[i][j][k]*(n-i)*j/(n*m-k);
					dp[i][j+1][k+1]+=dp[i][j][k]*(m-j)*i/(n*m-k);
					dp[i+1][j+1][k+1]+=dp[i][j][k]*(n-i)*(m-j)/(n*m-k); 
				}
			}
		}
		ans=0.0;
		int p=max(n,m);
		for(int i=p;i<=pmax;i++)
	    ans+=dp[n][m][i]*i;//天数*概率 
	    
		cout<<setiosflags(ios::fixed)<<setprecision(12)<<ans<<endl; 
	}
	return 0;
}


codefores D:
题意:给出石头、剪刀、布的个数,每次两个不同的会遇见,问经过足够长的时间后,分别单独留在场上的概率。

用dp[i][j][k]分别表示场上剩下的石头、剪刀、布的个数,根据代码中状态转移方程,最后只要场上 目标对象的 敌人为0时,求出概率和即是最终的获胜概率。

//D:
#include<iostream>
#include<iomanip>
using namespace std;
double dp[101][101][101];
int main()
{
	int a,b,c;
	cin>>a>>b>>c;
	dp[a][b][c]=1.0;
	for(int i=a;i>=1;i--)
	for(int j=b;j>=1;j--)
	for(int k=c;k>=1;k--)
	{
		if(dp[i][j][k]==0)
		continue;
		int tot=i*j+i*k+j*k; //总的情况 
		dp[i-1][j][k]+=i*k*1.0/tot*dp[i][j][k];
		dp[i][j-1][k]+=j*i*1.0/tot*dp[i][j][k];
		dp[i][j][k-1]+=k*j*1.0/tot*dp[i][j][k];
	}
	double ans=0;
	for(int i=1;i<=a;i++)
	for(int j=0;j<=b;j++)
	ans+=dp[i][j][0];
	cout<<setiosflags(ios::fixed)<<setprecision(12)<<ans;
	ans=0;
	for(int i=1;i<=b;i++)
	for(int j=0;j<=c;j++)
	ans+=dp[0][i][j];
	cout<<" "<<setiosflags(ios::fixed)<<setprecision(12)<<ans;
	ans=0;
	for(int i=1;i<=a;i++)
	for(int j=1;j<=c;j++)
	ans+=dp[i][0][j];
	cout<<" "<<setiosflags(ios::fixed)<<setprecision(12)<<ans<<endl;
	
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值