掷骰子
作者身为一个初中生,学着高中的概率,感觉染色体快被挤爆了......概率的题,每一题,都有点网上题解的味道......
这道概率题,是很经典的DP概率
题目描述
玩家A和B正在玩骰子游戏。
A骰子有6个面,第i个面的点数是sideA[i]。
B骰子有6个面,第i个面的点数是sideB[i]。
玩家A总共掷X次A骰子,每次掷骰子得到的面都是1/6的概率。
玩家B总共掷Y次B骰子,每次掷骰子得到的面都是1/6的概率。
玩家最终的总得分就是每次掷骰子得到的点数的总和。
计算玩家A赢得游戏的概率,即玩家A总得分高于玩家B的总得分的概率。
输入格式
组测试数据。
第一行,一个整数G,表示有G组测试数据。 1 <= G <= 10
每组测试数据格式:
第一行,两个整数,X 和 Y。1 <= X,Y <= 200。
第二行,6个整数,第i个整数是sideA[i]。 1 <= sideA[i] <= 100。
第三行,6个整数,第i个整数是sideB[i]。 1 <= sideB[i] <= 100。
输出格式
共G行,共G行,每行一个实数,误差不超过0.0001。
输入/输出例子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
看完题目后,如果没有思路,那就直接DP吧
总结了初步的dp之后,发现dp的下标都跟题目的未知量有关,比如这题的题目未知量是得分和次数,而本题有两个次数,所以有两个dp数组。想一想这题的状态转移方程,那么f[i][j]表示掷骰子i次可以的到j得分的概率,那么是不是我上一次可以掷到j-sideA[i],然后这一次掷sideA[i]就可以得到这个j分了,那么想要在这一次得到sideA[i],有1/6的概率的得到sideA[i],所以我们就得到转移方程式:
f[i][j]+=f[i-1][j-a[k]]*(1.0/6.0);
其实关键的代码只有这一行,不过......
DP初始化还是很重要的
我们这一题的DP数组初始化,首先想到当我掷0次是得到0分的概率,这还用说嘛,不就是1吗,所以初始化应为f[0][0]=1。
好了,我知道你们“像小猫一样粘着”我的代码,那就来吧!!!
#include<bits/stdc++.h>
using namespace std;
int g,a[10],b[10],maxa=INT_MIN,maxb=INT_MIN;
double f[200][20000],f2[200][20000],sumb[200][20000],ans=0.0;
//注意要准备两个DP数组:f和f2,因为分别为X 和 Y的dp数组
int main(){
scanf("%d",&g);
while(g--){
int x,y;
maxa=maxb=INT_MIN;
memset(f,0,sizeof f);
memset(f2,0,sizeof f2);
scanf("%d%d",&x,&y);
for(int i=1;i<=6;i++){
scanf("%d",&a[i]);
maxa=max(maxa,a[i]);
}
for(int i=1;i<=6;i++){
scanf("%d",&b[i]);
maxb=max(maxb,b[i]);
}
maxa*=x;
maxb*=y;
ans=0.0;
f[0][0]=f2[0][0]=1.00;
for(int i=1;i<=x;i++){
for(int j=1;j<=maxa;j++){
for(int k=1;k<=6;k++){
f[i][j]+=f[i-1][j-a[k]]*(1.0/6.0);
}
}
}
for(int i=1;i<=y;i++){
for(int j=1;j<=maxb;j++){
for(int k=1;k<=6;k++){
f2[i][j]+=f2[i-1][j-b[k]]*(1.0/6.0);
}
}
}
for(int i=1;i<=maxa;i++){
for(int j=1;j<i;j++){//因为j的分数一定要比i的分数小(不明白的自己看题目要求什么)
ans+=f[x][i]*f2[y][j];
}
}
printf("%.16lf\n",ans);
}
return 0;
}