概率DP专题 (更新中)

点击打开链接

POJ  3071 FOOTBALL

题意:n<=7 2^n个人,知道p[i][j]:i打败j的概率.1和2比,3-4比..胜利者进入下一轮继续,问获胜概率最大是谁? 
设dp[i][j]第i个人赢j轮的概率 dp[i][j]+=dp[i][j-1]*dp[k][j-1]*p[i][k]  k范围? 

利用位运算来推导:2^n个人对应一棵二叉树,从下数第j层代表轮数,若第i个人在第j层的左子树中 则j层的右子树结点都可以在第j轮作为i的对手

找到规律 i>>(j-1)^1==k>>(j-1)  i,k表示第j层中的左右子树的中某个结点

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
const int N=2e3+20;
const int inf=2e8;
double dp[N][N];//dp[i][j]第i个人赢j轮的概率
double p[N][N]; 
int main()
{
	int n;
 	while(cin>>n&&n!=-1)  
 	{
 		int m=1<<n;
 		for(int i=0;i<m;i++)
 			for(int j=0;j<m;j++)
 				scanf("%lf",&p[i][j]);
 		int s,e;
 		memset(dp,0,sizeof(dp));
		for(int i=0;i<m;i++)
			dp[i][0]=1.0;
		for(int j=1;j<=n;j++)
		{
			for(int i=0;i<m;i++)
			{
				for(int k=0;k<m;k++)
				{
					//相邻 
					if(((i>>(j-1))^1)==(k>>(j-1)))
						dp[i][j]+=dp[i][j-1]*dp[k][j-1]*p[i][k];
				}
			}
		} 
		double res=0;
		int ans;
		for(int i=0;i<m;i++)
		{
			if(dp[i][n]>res)	
				res=dp[i][n],ans=i+1;
		}	
		cout<<ans<<endl;
	}
	
	return 0;
}


POJ  2151 Check the difficulty of Problems 

题意:T只队伍,m个问题,T<=1000,m<=30,p[i][j] 第i人解决问题j的概率,问冠军至少答对n题并且所有人都至少答对一题的概率?
P1:所有人都至少对一题 等于第i个人至少对一题累乘,求每个i时利用对立事件(1-对0题的概率)即可 
P2:所有人对的题目1<=x<=n-1的概率
P2为P1的子集 P1-P2为所有人都至少对一题 && 至少存在一个人(冠军)答题数>=n 
dp[k][i][j]第k个人前i题对j题的概率 

m*t<=3e4 暴力枚举第i人 答对的题为1~n-1 即可计算出P2 

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
const int N=1e3+20;
const int inf=2e8;
double dp[N][35][35];//dp[k][i][j]第k个人前i题对j题的概率
double p[N][N];
int m,t,n;
int main()
{
	while(cin>>m>>t>>n&&(m+t+n))
	{
		for(int i=1;i<=t;i++)
			for(int j=1;j<=m;j++)
				scanf("%lf",&p[i][j]);
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=t;i++)
			dp[i][0][0]=1;
		for(int k=1;k<=t;k++)
		{
			for(int i=1;i<=m;i++)
			{
				for(int j=0;j<=i;j++)
					dp[k][i][j]=dp[k][i-1][j]*(1.0-p[k][i])+(j-1<0?0:dp[k][i-1][j-1]*p[k][i]);
			}	
		}
		double ans=1;
		for(int k=1;k<=t;k++)
			ans*=(1.0-dp[k][m][0]);//第k个人至少对一题 	
		double res=1;
		for(int k=1;k<=t;k++)
		{
			double cnt=0;
			for(int i=1;i<n;i++)
			{
				cnt+=dp[k][m][i];
			}
			res*=cnt;
		}	
		if(n==1)
			res=0;
		ans-=res;
		printf("%.3lf\n",ans);
	}

	return 0;
}

Codeforces 540D Bad Luck Island

题意:a kill b  b kill c  c kill a 给出abc个数 问最后只剩下a的概率 a,b,c<=100

设dp[a][b][c] 表示三种种类为abc时 a获胜概率  按照每次选两个不同的种类来转移状态即可 .

也可以设dp[a][b][c]为还剩下abc时的概率,最后累加dp[i][j][0]即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> ii;
const int N=1e2+20;
const ll inf=2e15;
double f[N][N][N];
int a,b,c;
double DP(int a,int b,int c)
{
	if(a==0||(b==0&&c))
		return f[a][b][c]=0;
	if(f[a][b][c]!=-1)
		return f[a][b][c];
	if(c==0)
		return f[a][b][c]=1;
	double res=0,s=a+b+c,sum=a*(a-1)/2 + b*(b-1)/2+c*(c-1)/2;
	s=s*(s-1)/2.0;
	s-=sum;
	if(b&&c)
		res+=1.0*b*c/s*DP(a,b,c-1);	
	if(a&&b)
		res+=1.0*a*b/s*DP(a,b-1,c);	
	if(a&&c)
		res+=1.0*a*c/s*DP(a-1,b,c);
	return f[a][b][c]=res;	
}
void init()
{
	for(int i=0;i<=100;i++)
		for(int j=0;j<=100;j++)
			for(int k=0;k<=100;k++)	
				f[i][j][k]=-1;
}
int main()
{
	while(cin>>a>>b>>c)
	{
		double x,y,z;
		init(); 
		DP(a,b,c);
		x=f[a][b][c];
		init();
		DP(b,c,a);
		y=f[b][c][a];
		z=1-x-y;
		printf("%.12lf %.12lf %.12lf\n",x,y,z);
	}
	return 0;
}

Codeforces 148D Bag of Mice

题意:w个白球,b个黑球,P,D两人轮流抽球,先抽到白球的人胜利,D抽球时,剩下的球会随机消失掉一个(剧情需要..),若游戏结束没人抽到白球则D获胜.
w,b<=1000 问P获胜的概率?

设DP[w][b]为球个数为wb时 P获胜的概率 s=w+b
w/s直接获胜,否则P抽到黑球后再转移到D抽到黑球后的状态(P要转移到下一个状态决策时,说明游戏还没结束,即P还没输)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e3+5;
int a,b;
double d[N][N];
void init()
{
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
			d[i][j]=-1;
}
double DP(int a,int b)
{
//	printf("%d %d\n",a,b);
	if(a==0)
		return d[a][b]=0;
	if(d[a][b]!=-1)
		return d[a][b];	
	double res=0,s=a+b,num=1.0*b/s*(b-1)/(s-1);
	res+=1.0*a/s;
	if(b>=2)
		res+=1.0*num*a/(s-2)*DP(a-1,b-2);
	if(b>=3)
		res+=1.0*num*(b-2)/(s-2)*(DP(a,b-3));
	return d[a][b]=res;
}
int main()
{
	while(cin>>a>>b)
	{
		init();
		printf("%.9lf\n",DP(a,b));
	}
	return 0;
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值