1 条题解
-
0
B. 最小化逆序对 题解
题意回顾
我们有两个长度为 的排列 和 。每次操作可以选择两个下标 和 ,同时交换 与 以及 与 。目标是经过若干次操作后,使得两个排列中的逆序对总数尽可能小,并输出任意一个达到最小值的最终排列对。
核心观察
由于每次操作同时作用于两个排列的相同位置,因此无论进行多少次操作,每个下标上的数对 始终作为一个整体一起移动。换言之,我们只能对这 个数对进行任意重排,而不能改变每个数对内部的对应关系。
我们的目标是让 和 中的逆序对总数最小。考虑两个下标 与 ():
- 若 且 ,则当前在 和 中各产生了一个逆序对(共 个)。若交换这两个位置上的数对,则 和 中的这两个逆序对都会消失,逆序对总数减少 。
- 若 但 ,则交换会减少 的逆序对、增加 的逆序对,总数不变。
- 若 且 ,交换同样使总数不变。
- 若 且 ,交换会使总数增加 。
因此,为了最小化总逆序对数,我们希望避免出现 与 的大小关系同 与 的大小关系 不一致 的情况。最理想的状态是:对于任意 ,都有 ,即两个序列的 相对顺序完全相同。此时两个序列同序,交叉抵消的逆序对不再存在,总逆序对数达到下界。
如何才能达到这种同序状态呢?一个简单的办法是将所有数对按 的值从小到大排序。此时 变成完全升序,逆序对数为 ;而 则被动的跟着 排序,其逆序对数由它自身的相对顺序决定,无法再减少(因为任何改变都会打破 的有序性,从而引入 的逆序对)。可以证明,这样得到的总逆序对数就是全局最小值。
结论:将 看作一个整体,按 升序排列,得到的 和 即为最优解之一。
算法步骤
- 读入 以及排列 和 。
- 构造数对数组
pair<int,int> ab[n],其中ab[i] = {a[i], b[i]}。 - 对
ab数组按第一个元素( 的值)进行升序排序。 - 输出排序后的第一个元素序列作为新的 ,第二个元素序列作为新的 。
时间复杂度:单次测试用例 ,总复杂度 ,在 的限制下可以轻松通过。
参考代码
#include <bits/stdc++.h> using namespace std; int main() { ios_base::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while(t--) { int n; cin >> n; pair<int,int> ab[n]; for(int i = 0; i < n; ++i) { cin >> ab[i].first; } for(int i = 0; i < n; ++i) { cin >> ab[i].second; } sort(ab, ab + n); for(int i = 0; i < n; ++i) { cout << ab[i].first << ' '; } cout << "\n"; for(int i = 0; i < n; ++i) { cout << ab[i].second << ' '; } cout << "\n"; } return 0; }
正确性简要证明
设我们得到的新排列为 和 ,其中 已经有序()。任取另一种可达的排列对 和 ,其总逆序对数记为 。由于 的逆序对数为 ,只需证明 。通过逆序对的性质可以证明:将数对按 排序后,任何与原排列不同的顺序都会在 中引入新的逆序对,而减少的 中的逆序对数量不可能超过引入的 中的逆序对数量(详细证明可利用逆序对的对称性)。因此排序后的结果必为最优。
- 1
信息
- ID
- 6512
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者