信息学奥赛一本通 1239:统计数字 | 1847:【07NOIP提高组】统计数字 | OpenJudge NOI 2.4 7909 | 洛谷 P1097 [NOIP2007 提高组] 统计数字

【题目链接】

ybt 1239:统计数字
ybt 1847:【07NOIP提高组】统计数字
一本通中限制不许使用STL,那么引入头文件不能写,只能写,否则不允许提交。
OpenJudge NOI 2.4 7909:统计数字
洛谷 P1097 [NOIP2007 提高组] 统计数字

【题目考点】

1. 二分查找

2. 插入排序

【解题思路】

如果数字范围很小,用散列思想,设数组ct,ct[i]表示数字i出现的次数,遍历并计数即可。但该题数字范围很大,达到 1 0 9 10^9 109
如果设int型数组长度为 1 0 9 10^9 109,那么占用的内存空间为: 1 0 9 B ∗ 4 / 1024 / 1024 ≈ 3814 M B 10^9B*4/1024/1024\approx 3814MB 109B4/1024/10243814MB,而题目内存限制 65536 K B = 64 M 65536KB=64M 65536KB=64M,超出了题目给定的内存限制,所以不可行。
该题指明:不相同的数不超过10000个,可以以这一点为突破口。设一个数组a,以升序保存已经出现了的不相同的数字。另设一个数组ct,ct[i]为a[i]出现的个数。(或者设结构体,将数字和出现的个数合在一起处理)
要添加一个新的数字x时,有2种方法:

解法1:查找有序数组a中是否存在x

  • 如果存在x,找到值x的下标i,a[i]的个数增加1.
  • 如果不存在x,做插入排序中的一步,将数字x(以及ct中对应的元素)插入到有序序列中

解法2:查找有序数组a中大于等于x的最小元素的最小下标

假设找到下标i

  • 如果a[i]等于x,那么a[i]的个数增加1
  • 如果a[i]不等于x,那么应该将x插到第i位置。数组a从最末位置到第i位置依次向后移动一个位置,再让a[i] = x

解法3:先排序,后遍历

先用快排对所有数字排序,而后遍历整个数组,统计每种数字出现的个数并输出。

【题解代码】

解法1:查找有序数组a中是否存在x

#include //ybt该题不能引入
using namespace std;
#define N 10005
struct Num
{
	int n, c;//n:数字 c:个数 
};
Num a[N];
int n, x, ai;//ai:a中元素的个数
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &x);
        int l = 1, r = ai, m = 0;
        while(l <= r)
        {
            m = (l + r) / 2;
            if(x < a[m].n)
                r = m - 1;
            else if(x > a[m].n)
                l = m + 1;
            else
            	break;
        }
        if(m != 0 && a[m].n == x)
        	a[m].c++;
        else
        {//把x插入a
            a[++ai].n = x;
            a[ai].c = 1;
			for(int j = ai; j > 1; --j)
            {
            	if(a[j].n < a[j-1].n)
            		swap(a[j], a[j-1]);
                else
                	break;
        	}
        }
    }
    for(int i = 1; i <= ai; ++i)
        printf("%d %d\n", a[i].n, a[i].c);
    return 0;
}

解法2:查找有序数组a中大于等于x的最小元素的最小下标

#include //ybt该题不能引入
using namespace std;
#define N 10005
struct Num
{
	int n, c;//n:数字 c:个数 
};
Num a[N];
int n, x, ai;//ai:a中元素的个数
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &x);
        int l = 1, r = ai, m = 0;
        while(l < r)
        {
            m = (l + r) / 2;
            if(a[m].n >= x)
                r = m;
            else
                l = m + 1;
        }
        if(a[l].n < x)//如果序列中没有大于等于x的最小值,就把x放在末尾 
        {
        	a[++ai].n = x;
        	a[ai].c = 1;
        }
        else if(a[l].n == x)
        	a[l].c++;
        else
        {//把x插入a[l]
			for(int j = ai; j >= l; --j)
            	a[j+1] = a[j];
         	a[l].n = x;
         	a[l].c = 1;
         	++ai;
        } 
    }
    for(int i = 1; i <= ai; ++i)
        printf("%d %d\n", a[i].n, a[i].c);
    return 0;
}

解法3:先排序,后遍历

#include
using namespace std;
#define N 200005
int main()
{
	int n, a[N], ct = 0, num;
	cin >> n;
	for(int i = 1; i <= n; ++i)
		cin >> a[i];
	sort(a+1, a+1+n);
	num = a[1];
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] == num)
			ct++;
		else
		{
			cout << num << ' ' << ct << endl;
			ct = 1;
			num = a[i];
		}
	} 
	cout << num  << ' ' << ct << endl;
    return 0;
}

你可能感兴趣的:(信息学奥赛一本通题解,NOIP真题解答,洛谷题解,c++,二分)