• LFYZ-OJ ID: 1015 统计数字(NOIP2007)


    分析

    本体思路很简单:读入数据,排序、统计、输出。难点在于数据量较大,选择何种排序方法就极为重要,否则很容易发生内存或时间超限。可以考虑以下几种思路:

    1. 桶排序
      桶排序是可以想到的最简单方法,可在O(n)的时间内一次性完成排序和统计:tong[number]++;,考虑到本题中被统计的数字的范围0~1.5*10^9,就需要至少1.5*10^9大小的桶,这样大的一个int数组所占用的内存空间为6*10^9字节(32位计算机中一个int为4个字节),即6G Byte,已经远远超了128M Byte的要求,故桶排序无法使用。

    2. 冒泡排序
      冒泡排序是最简单的一种稳定性排序,时间复杂度为O(n2)。可以考虑将所有的数据读入一个数组中再进行排序、统计。根据题目已知n的范围:1~200000,按最坏情况考虑,O(n^2)=2*10^10,单核心CPU时钟频率数量级一般为GHz,即1s可以执行1*10^9条指令(这里仅为数量级上的估算,不同型号CPU在不同计算机系统下的具体计算速度是不一样的)。可见使用冒泡排序很难在1s内完成排序,需要更高效的排序方法。

    3. 插入排序
      插入排序的时间复杂度也是O(n2),但考虑这样一种情况,因为不相同的数字不超过10000个,对于已经存在的数字,我们简单的对其计数,对于不存在的数字,进行插入排序。具体分析如下:

      • 对于不相同的数字,最多进行10000个插入操作,相当于10000个数字进行插入排序,所需时间为1*10^8,这是CPU在1s内能够完成的。
      • 对于相同的数字,因为已经存在,只需要将其找出,并对其计数加1,如果使用顺程序查找的话,时间复杂度为O(n),在10000个数字里面查找一个数字,最坏情况选需要10000次,那么在最糟糕情况下:统计200000个数字,其中有10000次为插入&排序操作,190000次为查找计数操作,这190000次消耗时间最多为190000*10000=1.9*10^9,远超CPU单位时间(1s)内的运算能力。为了减少这部分时间,考虑到我们是在一个已拍好序的序列中查找,因此可以使用折半查找,每次查找的次数最多为log10000,最多14次,那么190000次查询消耗时间最多为190000*14=2.66*10^6,耗时最够低,满足我们的要求。
      • 算法思路如下:
        1. 初始化count=0,序列NUM[10001][2]={0};
        2. 读入一个数字number
        3. 在序列NUM中查找这个number并返回位置wz
        4. 如果wz==-1(不存在),将number以插入排序方式插入序列,count++
        5. 如果wz>0(存在),则该位置计数加1,NUM[wz][1]++;
        6. 返回第2步

    该思路是在一边插入、一边排序、一边统计,代码见例程1。

    1. 快速排序
      快速排序是一种很高效的排序方法:时间复杂度为nlogn,将n个数字进行排序的话最大时间为200000*log200000,约为3.6*10^6,可见效率极高,当然这个数字是最理想情况了,实际情况差点,但在数量级上一般不会差异过大。这里不再详细分析快速排序,可参考输油管道问题。代码见例程2。

    2. 使用algorithm算法库
      如果允许使用algorithm算法库,问题就更简单了,直接使用sort函数,有人问这个sort函数使用了什么算法,这就复杂了:STL中的sort在普通快速排序的基础上进行了优化,它还结合了插入排序和堆排序。根据不同的数量级别以及不同情况,能自动选用合适的排序方法。当数据量较大时采用快速排序,分段递归。一旦分段后的数据量小于某个阀值,为避免递归调用带来过大的额外负荷,便会改用插入排序。而如果递归层次过深,有出现最坏情况的倾向,还会改用堆排序。晕了吧?要知道,C++中的STL模板库可都是聪明人写出来的……不打击你了,感兴趣的话,自己去下载STL的源代码学习学习吧,本题使用sort的代码见例程3。

    例程1:插入排序和二分查找

    #include<iostream>
    using namespace std;
    
    int a[10001][2];					//存储序列,a[i][0]为数字,a[i][1]为计数
    int count=0;						//存储序列中的数字个数 
    
    int find(int num){					//二分查找 
    	int low=1, high=count, middle;
    	while(low<=high){
    		middle=(low+high)/2;
    		if(a[middle][0]==num)	return middle;
    		if(num<a[middle][0])	high=middle-1;
    		if(num>a[middle][0])	low=middle+1;
    	}
    	return -1;						//-1为未找到 
    }
    
    int main(){
    	int n;
    	scanf("%d", &n);
    	while(n>0){						//循环读入并处理n个数字
    		int num, wz;				//num临时存放读入的数字,wz为t在a中的位置 
    		scanf("%d", &num);
    		wz=find(num);
    		if(wz==-1){					//t没找到,插入到a中 
    			int pos=++count;		//pos为新插入点值,作为哨兵进行插入排序 
    			a[pos][0]=num;
    			a[pos][1]=1; 
    			while(pos>1 && a[pos][0]<a[pos-1][0]){
    				int t=a[pos][0];
    				a[pos][0]=a[pos-1][0];
    				a[pos-1][0]=t;
    				t=a[pos][1];
    				a[pos][1]=a[pos-1][1];
    				a[pos-1][1]=t;
    			}
    		}else a[wz][1]++;			//t找到了,计数加1 
    		n--;
    	}
    	for(int i=1; i<=count; i++)		//循环输出 
    		printf("%d %d
    ", a[i][0], a[i][1]);
    	return 0;
    }
    

    例程2:快速排序

    #include<iostream>
    using namespace std;
    
    int a[200001];
    void qsort(int l, int r){
    	int i,j,m,p;
    	i=l; j=r;                                   //i从左边开始找,j从右边开始找
    	m=a[(l+r)/2];                               //m为枢轴
    	while (i<=j)                                //循环条件
    	{
    		while (a[i]<m) i++;
    		while (a[j]>m) j--;
    		if (i<=j)
    		{
    			p=a[i]; a[i]=a[j]; a[j]=p;
    			i++; j--;
    		}
    	}
    	if (i<r) qsort(i,r);                        //递归和终止条件
    	if (l<j) qsort(l,j);                        //递归和终止条件
    }
    
    int main(){
    	int n;
    	cin>>n;
    	for(int i=1;i<=n;i++)	scanf("%d",&a[i]);
    	qsort(1,n);
    	for (int i=1, count=0; i<=n; ++i){
    		count++;								//累加计数 
    		if((i==n) || (a[i]!=a[i+1])){			//如果是最后一个或者下个数字不等于当前数字 
    			printf("%d %d
    ",a[i], count);		//输出 
    			count=0;							//count归0 
    		}
      	}
    	return 0;
    }
    
    

    例程3:使用algorithm库中的sort算法

    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    int main(){
      int n;
      int a[200001];
      scanf("%d",&n);
      for(int i=1; i<=n; ++i) scanf("%d",&a[i]);	//从下标1开始存放 
      sort(a+1, a+n+1);								//sort排序 
      for(int i=1,count=0; i<=n; ++i){
        count++;									//累加计数 
    	if ((i==n) || (a[i]!=a[i+1])){				//如果是最后一个或者下个数字不等于当前数字 
    		printf("%d %d
    ",a[i], count);			//输出 
    		count=0;								//sum归0 
    	}
      }
      return 0;
    }
    
  • 相关阅读:
    2008年Web2.0峰会:发展是绝对的硬道理
    盖茨"接班人":微软产品为何总是挨批
    如何使用命令方式检测mx记录是否生效
    IBM公布未来5年将改变人类生活的五大科技
    谷歌李开复:我的传奇人生源于十句箴言
    VCL已死,RAD已死(3)
    VCL已死,RAD已死(2)
    主要程序设计语言范型综论与概要
    谷歌正式放弃与雅虎的广告合作计划
    模仿google分页代码
  • 原文地址:https://www.cnblogs.com/lfyzoi/p/6834955.html
Copyright © 2020-2023  润新知