题意:输入一个1 ~ n (1≤n≤10000) 的排列,用不超过 96 次操作把它变成升序。每次操作都可以选一个长度为偶数的连续区间,交换前一半和后一半。例如,输入5,4,6,3,2,1,可以执行1,2先变成4,5,6,3,2,1,然后执行4,5变成4,5,6,2,3,1,然后执行5,6变成4,5,6,2,1,3然后执行4,5变成4,5,6,1,2,3,最后执行操作1,6即可。提示:2n次操作就足够了。(本段摘自《算法竞赛入门经典(第2版)》)
分析:
提示很重要,利用贪心的思想,每次处理当前最小的,处理过后就可以不管了。对于第i小的数而言,如果它在对应的位置,则不需要操作,如果不在对应的位置,可以验证最多只需要两次即可移动到对应的位置。因此移动次数不会超过2n。
代码:
#include <iostream>
#include <fstream>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <set>
#include <string>
using namespace std;
const int maxn = 100005;
int T, n, ans, pos, l;
int a[maxn];
vector< pair< int, int > > vec;
void change(int s, int e, int l)
{
for (int i = s; i < e; ++i)
swap(a[i], a[i + l]);
}
int main()
{
scanf("%d", &T);
for (int C = 0; C < T; ++C)
{
vec.clear();
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", &a[i]);
for (int i = 0; i < n; ++i)
{
pos = -1;
for (int j = i; j < n; ++j)
if (a[j] == i + 1)
{
pos = j;
break;
}
if (pos == i)
continue;
else if (n + i - pos - pos >= 0)
{
vec.push_back(make_pair(i + 1, pos + pos - i));
change(i, pos, pos - i);
}
else
{
l = (pos - i + 1) / 2;
vec.push_back(make_pair(pos - l - l + 2, pos + 1));
change(pos - l - l + 1, pos - l + 1, l);
vec.push_back(make_pair(i + 1, pos + pos - l - l - i));
change(i, pos - l, pos - l - i);
}
}
ans = vec.size();
printf("%d\n", ans);
for (int i = 0; i < ans; ++i)
printf("%d %d\n", vec[i].first, vec[i].second);
}
return 0;
}