Quartet Swapping
什么你说你智商不够注意不到特殊性质?其实这是道模拟题。我要让所有人都知道惊为天人的模拟做法!
由于要求字典序最小,贪心地做肯定是对的。奇数位的数只能移动到奇数位,偶数位的数只能移动到偶数位(这个总能注意到吧),因此对于每个数我们可以从所有奇数位 / 偶数位中挑一个最小的,然后尝试交换过来。
一但确定好要选择哪个数,就可以把在原数组上模拟,把目标数换过来。我们把相邻的两个看成一组,那这个交换过程就类似于冒泡排序,一定可以做到其他元素位置不变,只交换所需要的四个,所以可以一步到位直接换过来。而如果目标数字在最后一个不能分组,则可以先交换最后四个,把他换到前面来。
主播主播,那怎么快速找到那个最小的数是在哪里,并且可以动态维护他的位置呢?答案是使用 map
。我用了两个 multimap
,一个存奇下标,一个存偶下标,键为数字,值为数字所对应的位置,同时还用一个 vector
存下其对应的迭代器以支持随机访问。操作时同时交换 multimap
中的下标和 vector
中的迭代器,已经确定好位置的数直接从 multimap
中删掉就好了。
#include <cstddef>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
int main(void) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
size_t T;
fin >> T;
while (T--) {
size_t n;
fin >> n;
using It = multimap<ui, size_t>::iterator;
multimap<ui, size_t> pa, pb;
vector<It> a(n);
for (size_t i = 0; i < n; ++i) {
ui x;
fin >> x;
a[i] = (i % 2 == 0 ? pa : pb).emplace(--x, i);
}
auto operate = [&](It ta, It tb) {
swap(a[ta->second], a[tb->second]);
swap(ta->second, tb->second);
};
vector<ui> ans;
for (size_t i = 0; i < n - 3; ++i) {
auto pt = (i % 2 == 0 ? pa : pb).begin();
if (pt->second == n - 1)
operate(a[n - 4], a[n - 2]), operate(a[n - 3], a[n - 1]);
if (pt->second != i)
operate(a[i + 1], a[pt->second + 1]), operate(a[i], pt);
ans.emplace_back(pt->first);
(i % 2 == 0 ? pa : pb).erase(pt);
}
for (size_t i = n - 3; i < n; ++i) ans.emplace_back(a[i]->first);
for (auto i : ans) fout << i + 1 << ' ';
fout << '\n';
}
return 0;
}