概率dp学习

例题题单

练习题单


例题

A.Happy Running

题意: 操场总共下,跑圈需要先打卡A再打卡B,最终跑的距离超过k的概率。

![在这里插入图片描述](https://img-blog.csdnimg.cn/64acca246ddb46359ded44fe183e8f6e.jpeg在这里插入图片描述

除此外,两种情况的

void solve()
{
	double k,x;
	scanf("%lf %lf",&k,&x);
    double ans=0;
	if(k<x)ans=(1.0-k*k/(x*x))*0.5+0.5;
	else if(k==x)ans=0.5;
	else if(k<2*x) ans=(2*x-k)*(2*x-k)/(2*x*x);
    printf("%.2lf\n",ans);
	return;
}

B.恶意竞争

题意: 两家公司竞争找bug,共有n分类和s个系统,一个bug属于一个分类和一个系统,问找到所欲分类和系统至少都有一个bug的期望天数。

设dp[i][i]是已经找到的bug又i种,属于j个系统的期望天数。
那么dp[n][s]=0,dp[0][0]就是答案
状态转移:
找到的bug有四种情况:
1.属于已有的i个分类和j个系统(i/n) * (j/s)*(dp[i][j]+1)
2.属于已有系统,不属于已有分类(i/n) * (1-j/s)*(dp[i][j+1]+1)
3.属于已有分类,不属于已有系统(1-i/n) * (j/s)*(dp[i+1][j]+1)
4.既不属于已有分类,也不属于已有系统(1-i/n) * (1-j/s)*(dp[i+1][j+1]+1)
再化简

void solve()
{
	int n,s;
	scanf("%d %d",&n,&s);
	for(int i=n;i>=0;i--){
		for(int j=s;j>=0;j--){
			if(i==n&&j==s)continue;
			dp[i][j]+=(double)n*s/(n*s-i*j);//已有的i个分类和j个系统
            dp[i][j]+=(double)(n-i)*j*dp[i+1][j]/(n*s-i*j);//属于已有系统,不属于已有分类
            dp[i][j]+=(double)i*(s-j)*dp[i][j+1]/(n*s-i*j);//属于已有分类,不属于已有系统
            dp[i][j]+=(double)(n-i)*(s-j)*dp[i+1][j+1]/(n*s-i*j);//既不属于已有分类,也不属于已有系统
		}
	}
	printf("%.5lf",dp[0][0]);
	return;
}

C.带富翁

题意: 小明掷骰子,每走到一个点,就能够得到相应的点数,那么只能走到点数n,走到点数n的得分期望是多少?

设dp[i]为到i的得分期望
那么下一步能够得到i+1,i+2,i+3…i+6,只要是没有超过n就能够走到,并且每一种走法的概率都是1/6
那么当i+6>n时,筛子最多只能够掷到n-i,只有n-i种情况,每种情况

分类版:

void solve()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&a[i]);
		dp[i]=a[i];
	}
	for(int i=n-1;i>=1;i--){
		if(i+6<=n){//考虑当掷骰子为6都不会超过n,那么每个骰子都是等可能在下一次出现
			for(int j=1;j<=6;j++)
				dp[i]+=1.0/6*dp[i+j];
		}
		else{//否则下一个点数只可能有n-i种可能
			for(int j=1;j<=n-i;j++)
				dp[i]+=1.0/(n-i)*dp[i+j];
		}
	}
	printf("%.8lf\n",dp[1]);
	return;
}

简化版:

void solve()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&a[i]);
		dp[i]=a[i];
	}
	for(int i=n-1;i>=1;i--){
		double res=0;
		for(int j=1;j<=6;j++){
			if(i+j<=n)
				res+=dp[i+j];
		}
		dp[i]+=res/min(6,n-i);
	}
	printf("%.8lf\n",dp[1]);
	return;
}

D.筛子游戏

题意: 投三个骰子,点数分别能够为k1,k2,k3,每次能够得到点数和的得分。但是k1,k2,k3点数分别为a,b,c时就必须得分归零,问得分大于n的期望次数是多少。

设dp[i]是得分为i的次数期望
那么状态转移dp[i]= ( ∑ p k ∗ d p [ i + k ] ) + d p [ 0 ] ∗ p 0 + 1 \displaystyle \left( \sum_{}p_k*dp[i+k] \right)+dp[0]*p_0+1 (pkdp[i+k])+dp[0]p0+1
p k 表示加 k 的概率, p 0 表示归 0 的概率 p_k表示加k的概率,p_0表示归0的概率 pk表示加k的概率,p0表示归0的概率
因为以n为分界点,所以从n反过来推,dp[0]就是答案
其实 ( ∑ p k ∗ d p [ i + k ] ) \displaystyle \left( \sum_{}p_k*dp[i+k] \right) (pkdp[i+k])里面也存在dp[0],这时候也能够将式子设成
dp[i]=A[i]dp[0]+B[i]
dp[i]= ( ∑ p k ∗ A [ i + k ] ∗ d p [ 0 ] + p k ∗ B [ i + k ] ) + d p [ 0 ] ∗ p 0 + 1 \displaystyle \left( \sum_{}p_k*A[i+k]*dp[0]+p_k*B[i+k] \right)+dp[0]*p_0+1 (pkA[i+k]dp[0]+pkB[i+k])+dp[0]p0+1= ( ∑ p k ∗ A [ i + k ] + p 0 ) ∗ d p [ 0 ] + ( ∑ p k ∗ B [ i + k ] ) + 1 \displaystyle \left( \sum_{}p_k*A[i+k]+p_0 \right)*dp[0]+\left( \sum_{}p_k*B[i+k]\right)+1 (pkA[i+k]+p0)dp[0]+(pkB[i+k])+1

这样就方便求了

void solve()
{
	int n,k1,k2,k3,a,b,c;
	scanf("%d %d %d %d %d %d %d",&n,&k1,&k2,&k3,&a,&b,&c);
	double p0=1.0/(k1*k2*k3);
	for(int i=1;i<=k1;i++)
		for(int j=1;j<=k2;j++)
			for(int k=1;k<=k3;k++)
				if(i==a&&j==b&&k==c)continue;
					p[i+j+k]+=p0;
	int k=k1+k2+k3;
	for(int i=n;~i;i--){
		for(int j=3;j<=k;j++){
			A[i]+=p[j]*A[i+j];
			B[i]+=p[j]*B[i+j];
		}
		B[i]++,A[i]+=p0;
	}
	printf("%.10lf\n",B[0]/(1-A[0]));
	return;
}

E.食堂

题意:
吉吉国王偶尔会回想起自己的高中时代。在吉吉国王的高中时代,下课后冲向食堂是每个学生的基本操作,但是总得有人失败,为什么不能是我,实际上吉吉国王在打饭这件事上也是失败过很多次,比如没带饭卡,走错窗口,甚至食堂关门。
吉吉国王的高中食堂排队可以看成一个长度为nn的队列,一开始吉吉国王站在mm这个位置上,一般来说,窗口前的第一个人在打饭的时候会发生四种情况。
第一种情况是打饭的时候窗口没人,这个时候要等待一会儿,发生的概率是p1
第二种情况是发现自己没带饭卡,这个时候就要回去拿饭卡并且排到了队列的末尾,发生的概率是p2(这里认为每个人只有在即将打饭的时候才会去摸饭卡,只有这时才有发现自己没带饭卡的机会。)
第三种情况是打饭成功,这个时候队列的长度减一,发生的概率是p3
第四种情况是食堂关门,这个时候大家都不能打饭了,发生的概率是p4
吉吉国王老倒霉蛋了,经常在食堂关门的时候排在队伍的前面,因此他想知道这样的事件发生的概率。现在你需要告诉吉吉国王在食堂关门时他排在队伍的前k位的概率。

设dp[i][j]表示i个人排队,吉吉国王排在第j个位置,达到目标状态的概率(j<=i)
dp[n][m]就是所求的答案
j==1时,dp[i][1]=p1×dp[i][j]+p2×dp[i][i]+p4
2<=j<=k,dp[i][j]=p1×dp[i][j]+p2×dp[i][j-1]+p3×dp[i-1][j-1]+p4
k<j<=i,dp[i][j]=p1×dp[i][j]+p2×dp[i][j-1]+p3×dp[i-1][j-1]
式子两边都有左边的值,则要将式子化简
就需要提前求出p21=p2/(1-p2),p31=p3/(1-p3),p41=p4/(1-p4)…

void solve()
{
	cin>>n>>m>>k>>p1>>p2>>p3>>p4;
	double p21=p2/(1.0-p1),p31=p3/(1.0-p1),p41=p4/(1.0-p1);
	for(int i=1;i<=n;i++){
        vector<double>A(n+1),B(n+1);
		A[1]=p21,B[1]=p41;
		for(int j=2;j<=i;j++){
			if(j<=k){
				A[j]=A[j-1]*p21;
				B[j]=B[j-1]*p21+dp[i-1][j-1]*p31+p41;
			}
			else{
				A[j]=A[j-1]*p21;
				B[j]=B[j-1]*p21+dp[i-1][j-1]*p31;
			}
		}
		dp[i][i]=B[i]/(1-A[i]);//提前求出dp[i][i]
		for(int j=1;j<i;j++){
			if(j==1)dp[i][j]=dp[i][i]*p21+p41;
			else if(j<=k)dp[i][j]=dp[i][j-1]*p21+dp[i-1][j-1]*p31+p41;
			else dp[i][j]=dp[i][j-1]*p21+dp[i-1][j-1]*p31;
		}
	}	
	printf("%.5lf",dp[n][m]);
	return;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值