[BZOJ 2456] mode 常数时间求众数/队列压缩

题目传送门:【BZOJ 2456】


题目大意: 给你一个 n 个数的数列,其中某个数出现了超过 n/2 次即为众数。请你找出那个数。数据保证存在这样的众数。
时间限制:1 Sec
空间限制:1 MB


题目分析:

又是最近才考过的题的“原版”。果然“原版”要简单多了,比考试的题代码要少好几十行。。。

由题,这道题的难点在于空间限制,仅 1MB 的空间意味着我们不能用普通的“数组保存法”解决这道题。观察题目性质,这道题只需要让我们求出哪一个数是众数就行了,并且这里的众数出现次数还超过了 n/2。

那么这里我们就有一个好方法了:有一种叫做“抵消”的思想,我们将其类比在这道题上。因为其他所有的数出现次数之和都小于这里的众数,所以,假设每一个其他的数都可以与这个众数相互“抵消”掉,那么最后这个众数肯定还有剩余。到了这里,我们就可以很好地搞定这道题了:用一个 struct,里面存两个变量:一个是 id,表示当前可能的众数是什么;另一个是 cnt,表示当前可能的众数出现的次数(不是字面意思)。

具体的操作是这样的:我们先把 cnt 初始化为 0,在读入所有的数的过程中,设当前读入的数为 x;如果 cnt==0,那么就令 id=x,cnt=1;否则,若 id==x,则 cnt++,如果不等于,cnt–。最后输出保存下来的 id 值即可。

那么它的核心思想是什么呢?就是用了一种“优先队列”的思想。在这里因为空间不够多,我们就把队列压缩成了两个单独的变量,本质上是相似的。

下面附上代码:

[cpp] view plain copy
print ?
  1. #include  
  2.   
  3. struct Num{  
  4.     int id,cnt;                     //id:众数的编号   cnt:这个数出现的次数   
  5. }num;  
  6. int n,a;  
  7.   
  8. int main(){  
  9.     scanf(”%d”,&n);  
  10.     for (int i=1;i<=n;i++){  
  11.         scanf(”%d”,&a);  
  12.         if (a!=num.id)  
  13.             if (num.cnt>1) num.cnt–;  
  14.             else num.id=a,num.cnt=1;  
  15.         else num.cnt++;  
  16.     }  
  17.     printf(”%d”,num.id);  
  18.     return 0;  
  19. }  

你可能感兴趣的:(各大OJ专题(POJ,BZOJ,hdu等),总结篇,栈/队列/优先队列)