题目链接:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4486
#include <cstdio>
#include <utility>
#include <vector>
using namespace std;
// place[i]代表i所在的位置
int place[10010];
// 现在的排列
int array[10010];
// 交换的过程
vector<pair<int,int> > my_swap;
void do_swap(int left, int right);
void print_place();
int n;
int main()
{
int T;
scanf("%d", &T);
int count = 0;
while(count < T)
{
my_swap = vector<pair<int,int> >();
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &array[i]);
place[array[i]] = i;
}
// print_place();
// 依次归位每个元素
for(int i = 1; i <= n-1; i++)
{
if(place[i] != i)
{
// 如果该元素距离其初始位置大于整个序列的一半
// 以该元素为末尾,来将路程减半
if(place[i]-i > (n-i+1)/2)
{
if((place[i]-i+1) % 2 == 0)
{
my_swap.push_back(pair<int,int>(i, place[i]));
// printf("this here: (%d,%d)\n", i, place[i]);
// 实行交换
do_swap(i, place[i]);
}
else
{
my_swap.push_back(pair<int,int>(i+1, place[i]));
// printf("this here: (%d,%d)\n", i+1, place[i]);
// 实行交换
do_swap(i+1, place[i]);
}
}
// print_place();
// 当该元素距离其初始位置小于整个序列一半
// 将该元素移至正确位置
int end = place[i] + place[i]-i-1;
my_swap.push_back(pair<int,int>(i, end));
// printf("this: (%d,%d)\n", i, end);
do_swap(i, end);
// print_place();
}
}
// 输出
printf("%d\n", my_swap.size());
for(int i = 0; i < my_swap.size(); i++)
printf("%d %d\n", my_swap[i].first, my_swap[i].second);
count++;
}
return 0;
}
// 实行交换
// 交换array[left ... right]
void do_swap(int left, int right)
{
int num = (right-left+1)/2;
for(int i = 0; i < num; i++)
{
place[array[left+i]] = left+i+num;
place[array[left+i+num]] = left+i;
int t_num = array[left+i];
array[left+i] = array[left+i+num];
array[left+i+num] = t_num;
}
}
void print_place()
{
printf("now_array: ");
for(int i = 1; i <= n; i++)
printf("%d ", array[i]);
printf("\nnow_place: ");
for(int i = 1; i <= n; i++)
printf("%d:%d ", i, place[i]);
printf("\n");
}
感觉是道好题。
本质是将一堆数进行排序,可以用的操作只有交换两个相邻的序列。
最简单可以想到针对每个数,依次交换相邻位置,总可以让这个数到达正确的位置,此时相当于冒泡排序。
仔细想一下会发现,如果一个数的现在位置距离它的正确位置小于或等于序列的一半,那么直接以该数为第二个序列头,以正确位置上的数为第一个序列头,实行一次交换即可。
如果一个数的现在位置距离它的正确位置大于序列的一半,那么可以做的是尽量让该数先靠近正确位置,进行一次交换,可以使该距离至少折半,然后再进行一次上述交换即可。
这样会发现,交换的次数为O(2n)级别。而不是O(n^2).
不管是冒泡排序还是上述最多2次交换排序,重要的想法是:考虑每个数,将最大/最小的数移到正确位置后,再考虑剩下的序列。