• 概率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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    简化版:

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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
    式子两边都有左边的值,则要将式子化简
    就需要提前求出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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 相关阅读:
    学习笔记:机器学习之支持向量机(SVM)(上)
    OFDM系统各种调制阶数的QAM误码率(Symbol Error Rate)与 误比特率(Bit Error Rate)仿真结果
    springcache的使用详解(使用redis做分布式缓存)
    FPGA代码设计规范一些探讨
    Session&Cookie
    Swoole协程
    文档+PPT+源码等]精品基于springboot的线上跳蚤市场平台[包运行成功计算机毕业设计Java项目源码
    JavaSE类和对象
    37、引擎高可用方案
    《机器人SLAM导航核心技术与实战》第1季:第6章_机器人底盘
  • 原文地址:https://blog.csdn.net/m0_53735019/article/details/126290169