题意:
输入一个1~n的排列,用不超过9^6的操作把它变成升序。每次操作都可以选一个长度为偶数的连续区间,交换前一半和后一半。
思路:
俺们发现 例如按这个测试样例
5 4 6 3 2 1
我们要先把1移到第一个位置,然后就不管了,注意到1移到第一个位置后,不必再去动
无论如何,第一步将1移到中间偏左的位置
=> 3 2 1 5 4 6
然后从 1需要移动到的位置延伸到1的距离为 3 - 1 = 2
然后取 3 2 和 1 5交换位置
=> 1 5 3 2 4 6
同样的,将2移到中间偏左的位置,(注意这里的中间已经省去第一个位置了)
=>.1 5 2 3 4 6
然后取 2需要移动到的位置延伸到 2 的距离为1
然后取 5 2 交换位置
=> 1 2 5 3 4 6
细心的同学可能发现了 我也可以直接 1 5 3 2 4 6 直接变成 1 2 4 5 3 6 当然可以,但是为了简化代码,所以合并在一起
那么为什么总要移动到中间偏左的位置呢? 因为这样一定可以找到一个延伸距离,使数值从自身开始往后延伸不会碰到越界的情况
例如 3 2 4 5 1 6 比如我们要将1移到第一个位置,注意到我们可以移动 3 2 4 5 和 1 6 ? ? ,因此只要后面的距离够长,总能一步到位
所以
step 1 创建一个够长的后面距离
step 2 移动
这样就是LRJ 所说的2 * n的时间
这个方法要注意有一个BUG,在代码里面已经特判了。
#include<bits/stdc++.h>
#define LL long long
#define ms(s) memset(s, 0, sizeof(s))
using namespace std;
const int maxn = 1e4 + 10;
vector<pair<int, int>> vec;
int a[maxn];
int p[maxn];
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T--) {
vec.clear();
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
p[a[i]] = i;
}
for(int i = 1; i <= n; i++) {
if(i == a[i]) continue;
int pos = p[i];
//step 1
int mid = (i + pos) / 2;
int len = mid - i + 1;
if((i + pos) % 2 == 0) len--;
vec.push_back(make_pair(pos - 2 * len + 1,pos));
if(pos - 2 * len + 1 - pos != 1) {
for(int j = pos - 2 * len + 1; j <= mid; j++) {
swap(a[j], a[j + len]);
p[a[j]] = j;
p[a[j + len]] = j + len;
}
} else {
swap(a[pos - 2 * len + 1], a[pos]);
p[a[pos - 2 * len + 1]] = pos - 2 * len + 1;
p[a[pos]] = pos;
}
if(i == a[i]) continue;
len = p[i] - i;
vec.push_back(make_pair(i, i + 2 * len - 1));
for(int j = i; j < i + len; j++) {
swap(a[j], a[j + len]);
p[a[j]] = j;
p[a[j + len]] = j + len;
}
}
cout << vec.size() << endl;
for(auto& pair: vec) {
cout << pair.first << " " << pair.second << endl;
}
}
return 0;
}