[概率与期望值入门][掷骰子][动态规划]

文章讲述了如何通过状态转移方程和前缀和优化算法来计算玩家A在骰子游戏中,其总得分高于玩家B的概率,给出了C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

题目描述

输入格式

输出格式

输入/输出例子1

解题思路

最终代码


题目描述

玩家A和B正在玩骰子游戏.

A骰子有 6 个面,第 i 个面的点数是 sideA_i

B骰子有 6 个面,第 i 个面的点数是 sideB_i

玩家A总共掷 X 次A骰子,每次掷骰子得到的面都是 \huge \huge\frac{1}{6} 的概率

玩家B总共掷 Y 次B骰子,每次掷骰子得到的面都是 \huge \huge\frac{1}{6} 的概率.

玩家最终的总得分就是每次掷骰子得到的点数的总和.

计算玩家A赢得游戏的概率,即玩家A总得分高于玩家B的总得分的概率.

输入格式

G 组测试数据。

第一行,一个整数 \huge G,表示有 \huge G 组测试数据 . \huge 1 \le G \le 10.

每组测试数据格式: 

第一行,两个整数,\huge X 和 \huge Y.  \huge 1 \le X,Y \le 200.

第二行, 6 个整数,第 i 个整数是 sideA_i.  \huge 1 \le sideA_i \le 100.

第三行, 6 个整数,第 i 个整数是 sideB_i.  \huge 1 \le sideB_i \le 100.

输出格式

\huge G 行,共 \huge G 行,每行一个实数,误差不超过 \huge 10^{-4}.

输入/输出例子1

输入:

10

1 1

1 2 3 4 5 6 

1 2 3 4 5 6 

200 200

1 3 8 18 45 100 

1 4 10 21 53 100 

2 3

1 1 1 2 2 2 

1 1 1 1 1 1 

200 200

6 5 4 3 2 1 

3 4 6 5 1 2 

100 199

1 1 1 1 1 2 

1 1 1 1 1 1 

1 1

1 2 1 2 1 2 

2 1 2 1 2 1 

200 80

1 3 8 18 45 100 

1 4 10 21 53 100 

100 100

1 3 5 10 15 20 

9 9 9 9 9 9 

100 100

7 8 9 9 10 11 

1 3 5 10 15 20 

10 1

1 2 3 4 5 6 

59 70 80 90 95 100 

输出:

0.41666666666666663

0.25240407058279035

0.25

0.49416239842107595

1.5306467074865068E-78

0.25

0.9999999976160046

0.4943375131579816

0.49968090996086173

2.7563619479867007E-9


解题思路


显然,我们要先求出投掷完后两个玩家总得分的所有可能的概率.

用 \huge \huge f_{i,j} 表示玩家一投掷 \huge i 次后总得分为 \huge j 的概率.

则我们枚举第 \huge i 次投掷的得分为 \huge sideA_k.  \huge (1 \le k \le 6).

则状态转移方程为:

\huge f_{i,j}=\sum\limits_{k=1}^{1}{\frac{1}{6}f_{i-1,j-sideA_k}} (j \ge sideA_k)

再用 \huge g_{i,j} 表示玩家二投掷 \huge i 次后总得分为 \huge j 的概率

同理,状态转移方程为:

\huge g_{i,j}=\sum\limits_{k=1}^{6}{\frac{1}{6}g_{i-1,j-sideB_k}}(j \ge sideB_k)


设玩家A最终总得分为 \huge x ,玩家B最终总得分为 \huge y,此种情况的概率为 \huge P(x,y)

则 \huge P(x,y)=f_{X,x} \times g_{Y,y}

设集合 \huge ScoreA 仅包含玩家A最终所有可能得分,集合 \huge ScoreB 仅包含玩家B最终所有可能得分

则最终答案为  \huge \sum\limits_{x \in ScoreA,y \in ScoreB,x>y}{P(x,y)}

但是直接枚举两个玩家得分时间复杂度会过高,这里使用前缀和进行优化

最终代码

#include<bits/stdc++.h>
using namespace std;
const int N=201;
const int M=20001;
const double P=1.0/6.0;
int G,x,y,bagA,bagB,sideA[N],sideB[N];
double res,g1[N][M],g2[N][M],sum[M];
int main(){
    scanf("%d",&G);
    while(G--){
    	memset(g1,0,sizeof(g1));
    	memset(g2,0,sizeof(g2));
    	bagA=bagB=0;
		res=0.0;
    	scanf("%d%d",&x,&y);
    	for(int i=1;i<=6;i++) scanf("%d",&sideA[i]),bagA=max(bagA,sideA[i]);
    	for(int i=1;i<=6;i++) scanf("%d",&sideB[i]),bagB=max(bagB,sideB[i]);
    	bagA*=x;bagB*=y;
    	g1[0][0]=g2[0][0]=1.0;
    	for(int i=1;i<=x;i++)
    		for(int j=1;j<=bagA;j++)
    			for(int k=1;k<=6;k++){
    				if(sideA[k]>j) continue;
    				g1[i][j]+=g1[i-1][j-sideA[k]]*P;
				}
		for(int i=1;i<=y;i++)
    		for(int j=1;j<=bagB;j++)
    			for(int k=1;k<=6;k++){
    				if(sideB[k]>j) continue;
    				g2[i][j]+=g2[i-1][j-sideB[k]]*P;
				}
		for(int i=1;i<=bagA;i++) sum[i]=sum[i-1]+g2[y][i];
		for(int i=1;i<=bagA;i++) res+=g1[x][i]*sum[i-1];
		printf("%lf\n",res);
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值