P2911 [USACO08OCT]Bovine Bones G

题面翻译

贝茜喜欢玩棋盘游戏和角色扮演游戏,所以她说服了约翰开车带她去小商店.在那里她买了三个骰子。这三个不同的骰子的面数分别为 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 2s120 2 ≤ s 2 ≤ 20 2 \leq s_2\leq 20 2s220 2 ≤ s 3 ≤ 40 2 \leq s_3\leq 40 2s340

输入格式

* 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+s21种情况。

  • “点数之和”出现次数最多的是 s 1 + 1 、 s 1 + 2 、 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ s 2 、 s 2 + 1 s1+1、s1+2、······s2、s2+1 s1+1s1+2⋅⋅⋅⋅⋅⋅s2s2+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 s31+s2s1 )时,为了使“点数和”最小,我们要将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+s2s1 )时,
比如,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+s2s1 )时,为了使“点数和”最小,我们要将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+s2s1)÷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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值