题面翻译
贝茜喜欢玩棋盘游戏和角色扮演游戏,所以她说服了约翰开车带她去小商店.在那里她买了三个骰子。这三个不同的骰子的面数分别为 s 1 , s 2 , s 3 s_1,s_2,s_3 s1,s2,s3。
对于一个有 S S S 个面的骰子每个面上的点数是 1 , 2 , 3 , … , S 1,2,3,\ldots,S 1,2,3,…,S。每个面(上的点数)出现的概率均等。贝茜希望找出在所有“三个面上的点数的和”中,哪个和的值出现的概率最大。
现在给出每个骰子的面数,需要求出哪个所有“三个面上的点数的和”出现得最频繁。如果有很多个和出现的概率相同,那么只需要输出最小的那个。
数据范围: 2 ≤ s 1 ≤ 20 2\le s_1\leq 20 2≤s1≤20, 2 ≤ s 2 ≤ 20 2 \leq s_2\leq 20 2≤s2≤20, 2 ≤ s 3 ≤ 40 2 \leq s_3\leq 40 2≤s3≤40。
输入格式
* Line 1: Three space-separated integers: S1, S2, and S3
输出格式
* Line 1: The smallest integer sum that appears most frequently when the dice are rolled in every possible combination.
样例输入
3 2 3
样例输出
5
提示
Here are all the possible outcomes.
1 1 1 -> 3
1 2 1 -> 4
2 1 1 -> 4
2 2 1 -> 5
3 1 1 -> 5
3 2 1 -> 6
1 1 2 -> 4
1 2 2 -> 5
2 1 2 -> 5
2 2 2 -> 6
3 1 2 -> 6
3 2 2 -> 7
1 1 3 -> 5
1 2 3 -> 6
2 1 3 -> 6
2 2 3 -> 7
3 1 3 -> 7
3 2 3 -> 8
Both 5 and 6 appear most frequently (five times each), so 5 is the answer.
思路分析
出现的概率pi = i出现的次数m除以所有情况的次数n,即pi = m/n
由于所有情况的次数是固定的,所以,“比较概率大小”本质上就是比较“出现次数的大小”。所以,解题的时候,只需要统计每种点数之和出现的次数,然后找出出现次数最多的情况。
解法1:
本题的数据范围非常小: 2≤s1≤20,2≤s2≤20,2≤s3≤40。
202040 = 16000,时间复杂度也只有10^4,所以,可以用三层嵌套循环(暴力法)解决。
参考代码
#include<bits/stdc++.h>
using namespace std;
//用数组统计所有“三个个面上的点数和”出现的次数。“三个个面上的点数和”最小是3,最大是80,创建大小是100的数组就足够了。
int a[100];
int main() {
int s1, s2, s3;
cin >> s1 >> s2 >> s3;
//统计所有“三个个面上的点数和”出现的次数。
for (int i = 1; i <= s1; i++) {
for (int j = 1; j <= s2; j++) {
for (int k = 1; k <= s3; k++) {
a[i + j + k]++;
}
}
}
//找出哪个所有“三个面上的点数的和”出现得最频繁
int maxV = 0, pos = 0;
for (int i = 3; i <= 80; i++) {
//只有出现次数更多,才更新最大值,保证找出最小的那个“和”
if (a[i] > maxV) {
maxV = a[i];
pos = i;
}
}
cout << pos;
return 0;
}
解法2:数学分析法
当有2个骰子的时候, 假设第1个骰子的面数s1 = 4,第2个骰子的面数s2=6,那么,“两个面上的点数之和” 如下表所示。
(图1)
当s1=4,s2 = 6的时候,“两个面上的点数之和”出现次数最多的是5、6、7,都是4次。
当s1=4,s2=11时,“两个面上的点数之和” 如下表所示。
(图2)
“两个面上的点数之和”出现次数最多的是5、6、7、8、9、10、11、12,都是4次。
从上面的情况可以发现,当s1与s2相差越大,出现次数最多的“点数和”的个数越多。
将“点数之和”放到数轴上,将其出现的次数作为高,可以画出这样一个“柱形图”。
(图3)
从图3的柱形图可知:
-
对于面数分别是s1、s2(s1 ≤ \le ≤
s2)的两个骰子,“点数之和”的可能情况有:2、3、4、······、s1、s1+1、······、s2、s2+1、······、s1+s2
, 总共 s 1 + s 2 − 1 s1+s2-1 s1+s2−1种情况。 -
“点数之和”出现次数最多的是 s 1 + 1 、 s 1 + 2 、 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ s 2 、 s 2 + 1 s1+1、s1+2、······s2、s2+1 s1+1、s1+2、⋅⋅⋅⋅⋅⋅s2、s2+1,都是s1次。
(图4) -
“点数之和” 2、3、······、s1出现的次数依次递增:1、2、······、s1-1
-
“点数之和” s2+2、······、s1+s2 出现的次依次递减:s1-1、s1-2、······、1。
在上面对两个骰子分析的基础上,我们添加第3个骰子。假设第3个骰子的面数是s3,面上的点数依次是1、2、3、······、s3。
第3个骰子的点数也可以被画成一个高度都是1柱状图。
(图5)
将图5放到图3的上方滑动,就可以分析“3个面的点数之和”的情况了。
在图3的上方滑动时,为了让“点数和”出现的次数最多,我们让这个柱状图的点数从右到左依次是1、2、3、······、s3。
(图6)
当s3=5时,“点数和”出现次数最多20次。“点数和”分别是10、11、12、13。
(注意,10那一列不能算,因为不是3个骰子的点数之和。)
上方图形“点数和”是10的情况
上方图形“点数和”是11的情况
上方图形“点数和”是12的情况
上方图形“点数和”是13的情况
题目要求输出“点数和”的最小值,就是10。
一般地,当s3的长度不超出
[
s
1
+
1
,
s
2
+
1
]
[s1+1,s2+1]
[s1+1,s2+1] 的范围时(即
s
3
≤
1
+
s
2
−
s
1
s3 \le 1+s2-s1
s3≤1+s2−s1 )时,为了使“点数和”最小,我们要将s3的左端点与 1+s1 重合,那么答案就是
1
+
s
1
+
s
3
1+s1+s3
1+s1+s3 ;
当s3的长度超出
[
s
1
+
1
,
s
2
+
1
]
[s1+1,s2+1]
[s1+1,s2+1] 的范围时(即
s
3
>
1
+
s
2
−
s
1
s3 > 1+s2-s1
s3>1+s2−s1 )时,
比如,s3=12,那么,s3滑动的过程如下。
当12与2对齐时,“点数和”14出现了41次。
当12与3对齐时,“点数和”15出现了42次。
当12与4对齐时,“点数和”16出现了41次。
所以,为了使“点数和”最小,我们要将s3 与区间 [ s 1 + 1 , s 2 + 1 ] [s1+1,s2+1] [s1+1,s2+1] 中间对齐。
当s3是奇数的时候,无法中间对齐,那么,就要让s3在区间
[
s
1
+
1
,
s
2
+
1
]
[s1+1,s2+1]
[s1+1,s2+1] 中间靠左的位置。比如,s3=11时,11与3对齐时,是出现的次数最多,并且“点数和”最小的情况。
综上,当s3的长度超出
[
s
1
+
1
,
s
2
+
1
]
[s1+1,s2+1]
[s1+1,s2+1] 的范围时(即
s
3
>
1
+
s
2
−
s
1
s3 > 1+s2-s1
s3>1+s2−s1 )时,为了使“点数和”最小,我们要将s3 与区间
[
s
1
+
1
,
s
2
+
1
]
[s1+1,s2+1]
[s1+1,s2+1] 尽量中间对齐。不能对齐时,就向左移一个位置。
这种情况下,“点数和”最小是
1
+
s
2
+
[
s
3
−
⌊
(
1
+
s
2
−
s
1
)
÷
2
⌋
]
+
1
1+s2 + [s3- \left\lfloor(1+s2-s1)\div2\right\rfloor ]+1
1+s2+[s3−⌊(1+s2−s1)÷2⌋]+1
当s1与s2相差越大,出现次数最多的“点数和”的个数越多,越方便我们找到答案。
因此,解题的时候,我们在读入s1、s2、s3后,要通过交换达到这样的大小关系:s1
≤
\le
≤ s3
≤
\le
≤s2。
参考代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int s1, s2, s3;
cin >> s1 >> s2 >> s3;
//交换目标:s1 <= s3 <= s2
if (s1 > s3) swap(s1, s3);
if (s3 > s2) swap(s2, s3);
if (s1 > s2) swap(s1, s2);
//根据s3 与 s2 + 1 - s1 的大小进行输出
if (s3 <= s2 + 1 - s1)
cout << 1 + s1 + s3;
else
cout << 1 + s2 + (s3-(1+s2-s1))/2+1; //int整除自动下取整
return 0;
}