洛谷P2397 yyy loves Maths VI (mode)

题目链接:P2397


题意

非常简单,就是求众数,并且保证众数出现的频率大于0.5。


思路

这个题目还是挺有意思的,有很多种思想可以解决,这里写几种我知道的

1. 排序+统计

通过排序可以使相同的数下标相邻,然后扫一遍就可以统计出众数了
时间复杂度根据排序的时间复杂度决定。应是 Θ ( n log ⁡ n ) \Theta(n \log n) Θ(nlogn) (应该并不能用桶排。。)

但是这题的 n n n 可以到2e6,这种方法还不够优秀
此外原题的空间限制实际上不够我们存下数组,严格来说这种方法就不可能通过

2. 抽样调查

如果说方法1是全面调查,那么我们自然会有抽样调查的方法——随机抽取下标,选出一部分数算出众数作为答案。
显然这个算法的复杂度是可以不超的。而bzy神仙告诉我们,在 n n n 很大的时候,这种方法也有着很低的错误率,因此这种方法可行。
实现的时候可以抽 1 20 \dfrac1{20} 201 左右的数。但是 n n n 太小的时候它会被 h a c k hack hack 。个人认为抽 min ⁡ ( 100000 , n ) \min(100000,n) min(100000,n) 个比较稳一些(当然方法不唯一

时间复杂度 Θ ( m log ⁡ m ) \Theta(m\log m) Θ(mlogm) ,其中 m m m 为样本容量

3. 摩尔投票法

这应该是最妙的一种方法了。时间 Θ ( n ) \Theta(n) Θ(n) ,空间 Θ ( 1 ) \Theta(1) Θ(1)
它的思想是,让不同的数两两抵消,最后剩下来的就是众数。
我们可以看一下这个命题:一个数列中众数出现的频率过半,那么从中去掉两个不同的数,众数不会改变。
这个命题显然正确,是真的显然。不信你自己想想qwq
那么根据这个命题,只要模拟一个两两抵消的过程,我们就可以得到结果。

具体操作起来是这样的
先设定一个当前数 x x x ,并存储它出现的次数 c n t cnt cnt (初值为0)。
假设现在读进来的数为 y y y
x ≠ y x \ne y x=y 时,显然可以两两抵消。如果 c n t = 0 cnt=0 cnt=0 ,说明 x x x 实际上已经没了,那么将 x x x 赋值为 y y y ;否则 − − c n t --cnt cnt
x = y x=y x=y 时, + + c n t ++cnt ++cnt
最后输出 x x x 即可。

4. 二进制拆分

在方法1中,桶排因为内存爆炸被淘汰了。但是为了利用起桶排的线性复杂度,我们可以使用二进制拆分。
因为 a i a_i ai 是32位无符号整数,我们把它按二进制拆分成32个数位。对于非0位,让这一位对应的桶+1。
然后把32个桶检查一遍,对于数量超过 n 2 \dfrac n2 2n 的就可以计入答案。
因为我们知道,如果答案的某一位为1,那么超过一半的数这一位都为1;如果答案的这一位为0,那么即使除了众数以外所有数这一位都为1,也不可能超过 n 2 \dfrac n2 2n

时间复杂度 Θ ( 32 n ) \Theta(32n) Θ(32n) 。看起来常数非常大,但是这个其实是32次位运算,卡一卡是可以过去的。
值得一提的是,如果题目中众数的出现频率不低于0.5,而不是大于0.5,那么这个方法不可行,因为在极限情况下会遇到桶内数值为 n 2 \dfrac n2 2n 的情况,而这就意味着答案的这一位无法被确定。


就写这么多吧
最后补一句,记得加读入优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值