【蓝桥杯 / C++】星际争霸 --- 字符串逆序对与大整数自定义排序
1. 题目概述
在星际争霸的世界中,Protoss 种族需要对一组战斗力数值数组 aaa 进行排序。
为了减少灵能干扰,排序规则如下:
- 第一关键字 :按字符串内部的逆序对数量升序排序。
- 第二关键字 :若逆序对数量相同,按数值大小升序排序。
输入要求:
- 数组长度 1≤n≤10001 \le n \le 10001≤n≤1000。
- 战斗力数值 1≤ai≤1010001 \le a_i \le 10^{1000}1≤ai≤101000,可能包含前导零。
- 输出时必须保留原前导零。
2. 解题思路与易错点
① 计算单个字符串的逆序对
- 题目中字符串最大长度为 1000,O(L2)O(L^2)O(L2) 的双重循环暴力统计逆序对数量,单次最多约 1000×9992≈5×105\frac{1000 \times 999}{2} \approx 5 \times 10^521000×999≈5×105 次比较,在 1s 时间限制内完全可以通过。
- 使用
long long记录逆序对数量,防止溢出。
② 处理前导零与大整数比较(核心易错点)
- 由于 ai≤101000a_i \le 10^{1000}ai≤101000,数值远超
long long范围,必须用字符串比较。 - 不能直接比较原字符串长度 :例如
"012"(长 3)与"45"(长 2),直接比长度会误判为 45<1245 < 1245<12。 - 解决方案 :利用 C++
std::string的成员函数find_first_not_of('0')去除前导零。- 去除前导零后,先比较有效位数长度;
- 若长度相同,再按字典序比较大小。
3. C++ 完整 AC 代码
cpp
#include <bits/stdc++.h>
using namespace std;
// 存储结构体
struct change {
string arr; // 原始字符串(含前导零)
long long sum; // 内部逆序对数量
};
// 1. 计算字符串内部的逆序对数
long long sum_change(const string &t) {
long long sum = 0;
int len = t.size();
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
if (t[i] > t[j]) {
sum++;
}
}
}
return sum;
}
// 2. 去除前导零函数(用于大整数比较)
string remove_leading_zeros(const string &s) {
auto k = s.find_first_not_of('0');
if (k == string::npos) { // 全是 '0'
return "0";
}
return s.substr(k); // 从第一个非0字符截取到末尾
}
// 3. 自定义排序比较规则
bool cmp(const change &a1, const change &a2) {
// 优先比较逆序对数量
if (a1.sum != a2.sum) {
return a1.sum < a2.sum;
}
// 逆序对相同时,比较去除前导零后的实际数值
string v1 = remove_leading_zeros(a1.arr);
string v2 = remove_leading_zeros(a2.arr);
if (v1.size() != v2.size()) {
return v1.size() < v2.size(); // 位数多的数值大
}
return v1 < v2; // 位数相同按字典序比较
}
int main() {
// 优化 I/O 效率
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<change> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i].arr;
a[i].sum = sum_change(a[i].arr);
}
// 自定义排序
sort(a.begin(), a.end(), cmp);
// 输出排序后的原字符串
for (const auto &m : a) {
cout << m.arr << "\n";
}
return 0;
}
4. 关键 API 总结
s.find_first_not_of('0'):查找字符串s中第一个不是'0'的字符位置。若全部为'0'则返回string::npos。s.substr(pos):截取从下标pos开始到末尾的子串。