出现次数超过一半的数

损坏的传送门

题目大意

给出一个含有 n ( 0 < n < = 1000 ) n(0 < n <= 1000) n0<n<=1000 个整数的数组,请找出其中出现次数超过一半的数。
数组中的数大于 − 50 -50 50 且小于 50 50 50

如果存在这样的数,输出这个数;否则输出 no

解题思路

显然,简单的模拟题,用一个桶记录一下就行了。

由于数组的下标不能为负数,所以全员加上 100 100 100 就行了,输出时在减去 100 100 100

就没了。。。

怎么可能!!!

来一道加强版

洛谷 P2397

共有 n n n 个正整数 a i a_i ai ,要你找一个众数,这个众数出现次数超过了一半。

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 2 × 1 0 6 1\le n \le 2\times 10^6 1n2×106 a i ∈ [ 1 , 2 31 ) a_i \in [1,2^{31}) ai[1,231)

这就需要介绍 摩尔投票法 了。

注 意 洛 谷 上 的 这 道 题 是 保 证 有 答 案 的 , 如 果 不 保 证 有 答 案 , 就 不 能 用 摩 尔 投 票 法 了 。 \color{red}{注意洛谷上的这道题是保证有答案的,如果不保证有答案,就不能用摩尔投票法了。}

摩尔投票法的基本思想很简单,在每一轮投票过程中,从数组中找出一对不同的元素,将其从数组中删除。这样不断的删除直到无法再进行投票,如果数组为空,则没有任何元素出现的次数超过该数组长度的一半。如果只存在一种元素,那么这个元素则可能为目标元素。

为了便于理解,我们想象一个情景,有一堆人在打架,我们假设每一个人的战斗力都是 1 1 1 1 1 1。也就是 A A A 打死 B B B A A A 也会死。

假设只有 2 2 2 波人(我们把不是众数的数字看作一个数)且各占二分之一,显然继续打下去两边人都会死光。

但是我们现在有 2 2 2 波人,其中一波人特别多(一半以上),显然这波人必然是赢家,虽然不知道打完还剩几个,但一定会有剩下的。因为一换一。若第二波人有 x x x 个,则第一波人有( n − x n-x nx)个且 n − x > x n-x>x nx>x。那么最后剩下的人就是 n − 2 x n-2x n2x 个。假设这 2 2 2 波人头上顶着 2 2 2 个不同的数字,这个剩下的人的数字就是众数。

以上用于帮助理解代码。

所以我们可以把摩尔投票法看作一种抵消的思想,当前数字进场,然后和上一个判断,如果是同一个数计数器 + + ++ ++,如果不是计数器 − − --

当计数器等于 0 0 0 时, a n s ans ans 重新登记为新输入的数,进行抵消的操作,最后剩下的一定是众数(但众数必须满足大于总数的一半)。

摩尔投票法其实就是随便找一个数 x x x,将他跟和他不同的数抵消:

  • 如果 x x x 不是答案,那么跟他不一样的数一定比他多,所以最后一定会找到答案的。

  • 如果 x x x 是答案,那么跟他不一样的数一定比他少,所以最后的答案一定会是他自己。

AC CODE

原题

#include<bits/stdc++.h>
using namespace std;
int n;
int a, b[10000];
bool flag = 0;
signed main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d", &a);
		b[a + 100] ++;
	}
	for(int i = 1; i <= 1000; ++i)
	{
		if((n % 2 == 0 && b[i] > n / 2) || (n % 2 == 1 && b[i] >= (n + 1) / 2))
		{
			printf("%d\n", i - 100);
			flag = 1;
			return 0;
		}
	}
	if(!flag)
	{
		printf("no\n");
	}
	return 0;
}

洛谷 P2397

#include <bits/stdc++.h>
using namespace std;
int n, x, ans, y = 0;
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &x);
		if (y == 0)
			ans = x;
		if (ans == x)
			y++;
		if (ans != x)
			y--;
	}
	printf("%d", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值