1 条题解
-
0
混合排序算法工作量计算(Out of Sorts)题解
一、问题核心分析
题目要求计算 Bessie 设计的
quickish_sort算法的总工作量(work_counter的最终值)。该算法的核心逻辑是:- 对当前数组反复执行
bubble_sort_pass(每次执行工作量累加数组长度),直到数组出现至少一个分隔点; - 按分隔点分割数组,对每个子数组递归执行上述过程。
分隔点的定义是:数组前
i个元素的最大值 ≤ 第i+1个元素之后的最小值。关键观察是:- 排序后的数组中,每个位置
i(1≤i≤n-1)都是分隔点(前i个元素是排序后的前i小值,后n-i个是后n-i大值); - 原算法的分隔点本质是“排序后数组中已就位的分割位置”,算法的核心是计算每个子数组需要执行的
bubble_sort_pass次数,再乘以子数组长度求和。
二、算法原理
1. 离散化处理
由于数组元素可能重复且范围大,先对数组元素离散化:
- 将原数组元素排序去重(保留索引信息),给每个元素分配一个排名(相同元素按原索引顺序排名),得到离散化后的数组
a,其中a[i]表示原数组第i个位置元素的排名; - 同时维护
inva数组,inva[r]表示排名为r的元素在原数组中的位置。
2. 关键变量定义
p[i]:表示排序后数组中“前i个元素”与“后n-i个元素”的分割位置(对应原数组的分隔点)所需的最少bubble_sort_pass次数;cnt1:统计当前未满足“分隔点条件”的元素个数,用于推导p[i];lst0:追踪排名为i的元素在原数组中的位置,用于更新cnt1。
3. 核心推导逻辑
排序后,前
i个元素的排名是1~i,后n-i个元素的排名是i+1~n。一个分割位置i成为分隔点的条件是:所有排名1~i的元素在原数组中的位置都在所有排名i+1~n的元素之前。bubble_sort_pass的本质是让较大元素“向右冒泡”、较小元素“向左冒泡”。每个bubble_sort_pass最多让一个逆序对中的较大元素右移一位。因此,p[i]等于“排名1~i的元素中,位于排名i+1~n元素左侧的逆序对数的最大值”,这一值可通过cnt1动态计算。4. 工作量计算
最终总工作量是所有子数组的“长度 × 所需
bubble_sort_pass次数”之和。通过p数组的特性,总工作量可简化为sum(max(p[i-1], p[i]))(i从 1 到n),其中p[0] = p[n] = 0。三、完整代码
#include <bits/stdc++.h> #define int long long #define mkp make_pair using namespace std; int n, a[100005], inva[100005], p[100005]; vector<pair<int, int>> vc; signed main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n; for (int i = 1; i <= n; i++) cin >> a[i], vc.push_back({a[i], i}); sort(vc.begin(), vc.end()); for (int j = 0; j < n; j++) a[vc[j].second] = j + 1, inva[j + 1] = vc[j].second; int lst0 = 0, cnt1 = 0; for (int i = 1; i < n; i++) { if (inva[i] < lst0) cnt1--; else { while (lst0 < inva[i]) { lst0++; if (a[lst0] > i) cnt1++; } } p[i] = max(1ll, cnt1); } int ans = 0; for (int i = 1; i <= n; i++) { ans += max(p[i - 1], p[i]); } cout << ans; return 0; }四、代码关键步骤解析
1. 离散化
- 读取原数组后,将每个元素与其索引组成 pair 存入
vc,按元素值排序(相同元素按索引排序); - 遍历排序后的
vc,给每个原数组位置i分配排名j+1(j是排序后的索引),存入a[i];同时inva[j+1] = i,记录排名对应的原位置。
2. 计算
p数组- 初始化
lst0 = 0(当前追踪的原数组位置)、cnt1 = 0(未满足分隔点条件的元素个数); - 遍历排名
i(1 到n-1):- 若排名
i的原位置inva[i]小于lst0,说明该元素已被之前的处理覆盖,cnt1减 1; - 否则,将
lst0推进到inva[i],途中若遇到“排名大于i的元素”(即属于后n-i个元素),则cnt1加 1(该元素需要被冒泡到右侧); p[i]取cnt1和 1 的最大值(至少需要 1 次冒泡才能产生分隔点)。
- 若排名
3. 计算总工作量
- 遍历
i从 1 到n,累加max(p[i-1], p[i])(p[0]和p[n]默认为 0); - 该累加和即为总工作量,直接输出。
五、复杂度分析
- 时间复杂度:。核心耗时为数组排序(离散化步骤),后续遍历均为 ;
- 空间复杂度:。用于存储原数组、离散化数组、
inva数组、p数组及排序用的 vector,可满足 的约束。
- 对当前数组反复执行
- 1
信息
- ID
- 5573
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者