题目大意
给出一个含有
n
(
0
<
n
<
=
1000
)
n(0 < n <= 1000)
n(0<n<=1000) 个整数的数组,请找出其中出现次数超过一半的数。
数组中的数大于
−
50
-50
−50 且小于
50
50
50。
如果存在这样的数,输出这个数;否则输出 no
。
解题思路
显然,简单的模拟题,用一个桶记录一下就行了。
由于数组的下标不能为负数,所以全员加上 100 100 100 就行了,输出时在减去 100 100 100。
就没了。。。
怎么可能!!!
来一道加强版
共有 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 1≤n≤2×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 n−x)个且 n − x > x n-x>x n−x>x。那么最后剩下的人就是 n − 2 x n-2x n−2x 个。假设这 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;
}