目录
题目描述
玩家A和B正在玩骰子游戏.
A骰子有 个面,第
个面的点数是
B骰子有 个面,第
个面的点数是
玩家A总共掷 次A骰子,每次掷骰子得到的面都是
的概率
玩家B总共掷 次B骰子,每次掷骰子得到的面都是
的概率.
玩家最终的总得分就是每次掷骰子得到的点数的总和.
计算玩家A赢得游戏的概率,即玩家A总得分高于玩家B的总得分的概率.
输入格式
组测试数据。
第一行,一个整数 ,表示有
组测试数据 .
.
每组测试数据格式:
第一行,两个整数, 和
.
.
第二行, 个整数,第
个整数是
.
.
第三行, 个整数,第
个整数是
.
.
输出格式
共 行,共
行,每行一个实数,误差不超过
.
输入/输出例子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
解题思路
显然,我们要先求出投掷完后两个玩家总得分的所有可能的概率.
用 表示玩家一投掷
次后总得分为
的概率.
则我们枚举第 次投掷的得分为
.
.
则状态转移方程为:
再用 表示玩家二投掷
次后总得分为
的概率
同理,状态转移方程为:
设玩家A最终总得分为 ,玩家B最终总得分为
,此种情况的概率为
则
设集合 仅包含玩家A最终所有可能得分,集合
仅包含玩家B最终所有可能得分
则最终答案为
但是直接枚举两个玩家得分时间复杂度会过高,这里使用前缀和进行优化
最终代码
#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;
}