​损坏的传送门​

题目大意

给出一个含有 \(n(0 < n <= 1000)\) 个整数的数组,请找出其中出现次数超过一半的数。

数组中的数大于 \(-50\) 且小于 \(50\)。

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

解题思路

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

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

就没了。。。

怎么可能!!!

来一道加强版

​洛谷 P2397​

共有 \(n\) 个正整数 \(a_i\) ,要你找一个众数,这个众数出现次数超过了一半。

对于 \(100\%\) 的数据,\(1\le n \le 2\times 10^6\),\(a_i \in [1,2^{31})\) 。

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

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

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

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

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

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

以上用于帮助理解代码。

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

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

摩尔投票法其实就是随便找一个数 \(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;
}