【算法刷题】蓝桥杯:0星际争霸(大数比较 + 多关键字自定义排序)
📌 题目链接与描述
- 题目名称:0星际争霸
- 题目链接 :蓝桥云课 - 0星际争霸
- 数据规模 : 1 ≤ n ≤ 1000 1 \le n \le 1000 1≤n≤1000, 1 ≤ a i ≤ 10 1000 1 \le a_i \le 10^{1000} 1≤ai≤101000(包含前导零,保留原格式输出)
- 核心问题 :给定 n n n 个数字字符串,按照以下三级优先级 进行升序排序:
- 第一优先级:字符串内部的"逆序对"数量(较小的排前面)。
- 第二优先级:逆序对相同时,按照数值大小排序(即字符串长度较短的排前面)。
- 第三优先级:长度相同时,按照字典序从小到大排序。
🔍 核心思路解析
1. 内部逆序对的计算
对于长度为 L L L 的字符串 S S S,统计内部所有满足 i < j i < j i<j 且 S i > S j Si > Sj Si>Sj 的字符对数量。
- 由于字符串长度 L ≤ 1000 L \le 1000 L≤1000,采用双重循环遍历的复杂度为 O ( L 2 ) \mathcal{O}(L^2) O(L2)。
- 1000 2 / 2 = 5 × 10 5 1000^2 / 2 = 5 \times 10^5 10002/2=5×105 次比较,运行耗时极短,完全不会超时。
2. 多关键字自定义排序规则(避坑关键)
当逆序对数量相同时,需要按数值大小排序。因为题目数据规模高达 10 1000 10^{1000} 101000,必须使用字符串存储:
- 不能直接比较字典序 :例如字符串
"9"和"10",如果仅使用字典序比较,"10" < "9"会得出错误结论;必须先比较字符串长度 ,长度短的数值小("9"长度为 1,"10"长度为 2)。 - 排序三步走 :
逆序对 r e v ⟶ 字符串长度 s i z e ( ) ⟶ 字典序 v a l \text{逆序对 } rev \longrightarrow \text{字符串长度 } size() \longrightarrow \text{字典序 } val 逆序对 rev⟶字符串长度 size()⟶字典序 val
💻 C++ 完整 AC 代码
cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 存储结构体
struct W {
string val; // 原始字符串
long long rev; // 内部逆序对数量
};
// 1. 计算单个字符串内部的逆序对数量
long long d_count(const string& a) {
long long cnt = 0;
int n = a.size();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (a[i] > a[j]) {
cnt++;
}
}
}
return cnt;
}
// 2. 自定义多关键字排序规则
bool cmp(const W& w1, const W& w2) {
// 优先级 1:逆序对数量升序
if (w1.rev != w2.rev) {
return w1.rev < w2.rev;
}
// 优先级 2:字符串长度升序(代表数值大小)
if (w1.val.size() != w2.val.size()) {
return w1.val.size() < w2.val.size();
}
// 优先级 3:长度相同时比字典序
return w1.val < w2.val;
}
int main() {
// 开启快速 I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<W> w(n);
for (int i = 0; i < n; i++) {
cin >> w[i].val;
w[i].rev = d_count(w[i].val); // 预计算逆序对
}
// 排序
sort(w.begin(), w.end(), cmp);
// 输出
for (int i = 0; i < n; i++) {
cout << w[i].val << "\n";
}
return 0;
}
❌ 易错点总结
- 漏掉"字符串长度"比较 :直接使用
w1.val < w2.val会导致"10"排在"9"前面(字典序错误)。 - 逆序对变量类型溢出 :当字符串长度达 1000 时,最大逆序对数可能达到 ≈ 5 × 10 5 \approx 5 \times 10^5 ≈5×105,累计总数虽不会溢出
int,但建议统一写成long long保证健壮性。
⏱️ 复杂度分析
- 时间复杂度 : O ( n ⋅ L 2 + n log n ⋅ L ) \mathcal{O}(n \cdot L^2 + n \log n \cdot L) O(n⋅L2+nlogn⋅L)
- 预处理 :计算 n n n 个长度为 L L L 的字符串逆序对,耗时 O ( n ⋅ L 2 ) \mathcal{O}(n \cdot L^2) O(n⋅L2)。
- 排序瓶颈 :排序比较共进行 O ( n log n ) \mathcal{O}(n \log n) O(nlogn) 次,每次比较耗时 O ( L ) \mathcal{O}(L) O(L)。
- 实际测试数据中,总运行时间控制在 20ms~160ms 之间,轻松 AC。
- 空间复杂度 : O ( n ⋅ L ) \mathcal{O}(n \cdot L) O(n⋅L),用于存储 n n n 个长为 L L L 的字符串结构体数组。