P1097 [NOIP 2007 提高组] 统计数字

记录38

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a[2000010]={},n,cnt;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		cout<<a[i]<<" ";
    cnt=1;
		while(a[i]==a[++i]&&i<=n) cnt++;
    i--;
    cout<<cnt<<endl;
	}
	return 0;
} 

突破点

统计这些自然数各自出现的次数

👉统计数字

并按照自然数从小到大的顺序输出统计结果

👉排序


思路

  1. 用数组存数字
  2. 用sort进行排序
  3. 统计连续出现的数字

代码简析

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a[2000010]={},n,cnt;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	...
	return 0;
} 

sort()对数组进行排序,关于sort排序,在文章的补充部分进行详细介绍

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a[2000010]={},n,cnt;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		cout<<a[i]<<" ";
    cnt=1;
		while(a[i]==a[++i]&&i<=n) cnt++;
    i--;
    cout<<cnt<<endl;
	}
	return 0;
} 

cout<<a[i]<<" ";        先输出数字

cnt=1;   第一个数为1个

while(a[i]==a[++i]&&i<=n) cnt++;       后一个等于前一个,++i跳出while循环,cnt进行计数

i--;    数不连续时跳出循环,然后退回上一个重新计数(for循环会重新加一次)

补充

sort() 函数完整用法指南


1. 基本语法与头文件

#include <algorithm>  // 必须包含

// 对数组排序
int arr[] = {3, 1, 4, 1, 5, 9};
sort(arr, arr + 6);   // 排序整个数组

// 对vector排序
vector<int> v = {3, 1, 4, 1, 5, 9};
sort(v.begin(), v.end());  // 排序整个vector

核心规则sort(begin, end)左闭右开区间 [begin, end) 排序。


2. 排序范围控制

int arr[] = {3, 1, 4, 1, 5, 9};

sort(arr, arr + 3);      // 只排序前3个元素 → {1, 3, 4, 1, 5, 9}
sort(arr + 2, arr + 5);  // 排序第2到第4个 → {3, 1, 1, 4, 5, 9}

竞赛技巧sort(arr, arr + n) 是排序前n个元素的标准写法。


3. 默认排序顺序
  • 默认升序:从小到大排序

  • 内置类型int, double, char, string 等直接支持

int a[] = {5, 2, 8};
sort(a, a + 3);  // 结果:{2, 5, 8}

string s = "bacd";
sort(s.begin(), s.end());  // 结果:"abcd"

4. 降序排序(三种方法)

方法A:使用 greater<>()

#include <functional>

int arr[] = {3, 1, 4};
sort(arr, arr + 3, greater<int>());  // 结果:{4, 3, 1}

方法B:自定义比较函数

bool cmp(int a, int b) {
    return a > b;  // 降序
}
sort(arr, arr + 3, cmp);  // 结果:{4, 3, 1}

方法C:Lambda表达式(C++11推荐)

sort(arr, arr + 3, [](int a, int b) {
    return a > b;
});

5. 结构体排序(竞赛核心)

方法A:重载 < 运算符(最常用)

struct Student {
    int id, score;
    // 按分数降序,分数相同按id升序
    bool operator < (const Student& other) const {
        if (score != other.score) return score > other.score;
        return id < other.id;
    }
};

vector<Student> students;
sort(students.begin(), students.end());  // 自动调用operator<

方法B:自定义比较器

bool cmp(const Student& a, const Student& b) {
    if (a.score != b.score) return a.score > b.score;
    return a.id < b.id;
}
sort(students.begin(), students.end(), cmp);

方法C:Lambda表达式

sort(students.begin(), students.end(), [](const Student& a, const Student& b) {
    if (a.score != b.score) return a.score > b.score;
    return a.id < b.id;
});

6. 稳定排序 stable_sort()

当需要保持相等元素的原始顺序时使用:

struct Node { int val, idx; };
vector<Node> v = {{5,0}, {3,1}, {5,2}};

// 按val排序,val相同时保持idx小的在前
stable_sort(v.begin(), v.end(), [](const Node& a, const Node& b) {
    return a.val < b.val;
});
// 结果:{{3,1}, {5,0}, {5,2}}(稳定)

性能:时间复杂度 O(n log n),但比 sort 慢,内存占用更多。


7. 部分排序 partial_sort()

当只需前k个有序时使用,比全排序更快

vector<int> v = {9, 2, 7, 4, 1, 8, 3};

// 只排序前3个最小元素到前面
partial_sort(v.begin(), v.begin() + 3, v.end());

// 结果:v = {1, 2, 3, 9, 7, 8, 4}(前3有序,其余未排序)

8. 性能分析
  • 时间复杂度:平均 O(n log n),最坏 O(n log n)

  • 空间复杂度:O(log n) 递归栈空间(内省排序)

  • 稳定性sort不稳定排序(相同元素可能改变顺序)


9. 常见错误与陷阱

错误1:比较器不满足严格弱序

// ❌ 错误:缺少等值处理
bool cmp(int a, int b) { return a <= b; }  // 导致段错误

// ✅ 正确
bool cmp(int a, int b) { return a < b; }

错误2:比较器依赖外部可变状态

int flag = 1;
bool cmp(int a, int b) {
    return flag ? a > b : a < b;  // ❌ 运行时修改flag会导致UB
}

错误3:对list排序

list<int> lst;
sort(lst.begin(), lst.end());  // ❌ list不支持随机访问迭代器

// ✅ 正确
lst.sort();  // list自带成员函数sort

10. 竞赛最佳实践

万能模板

// 结构体排序模板
struct Node {
    int x, y, id;
    bool operator < (const Node& other) const {
        if (x != other.x) return x < other.x;  // 第一关键字升序
        if (y != other.y) return y > other.y;  // 第二关键字降序
        return id < other.id;                   // 第三关键字升序
    }
};

// Lambda排序模板(更灵活)
sort(v.begin(), v.end(), [](const Node& a, const Node& b) {
    return a.x < b.x;
});

性能优化

  • 小规模数据(n < 1000):sortstable_sort 差异不大

  • 大规模数据(n > 10^5):优先 sort,避免 stable_sort

  • 部分有序sort 仍然高效,但 partial_sort 更快(只需前k个时)

可读性建议:简单逻辑用重载运算符,复杂逻辑用Lambda,避免单独定义比较函数。


核心要点总结

  1. 区间左闭右开sort(begin, end) 不包含 end

  2. 默认升序:内置类型无需比较器

  3. 降序三选一greater<>, 自定义函数, Lambda

  4. 结构体必重载operator < 是竞赛最速写法

  5. 稳定性需求:用 stable_sort

  6. 性能保证:O(n log n) 适合所有竞赛场景

  7. 严格弱序:比较器必须满足 a<bb<a 不能同时成立

掌握以上用法,可覆盖CSP竞赛中所有排序需求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值