题目:
输入一个1~n(1≤n≤300)的排列,用不超过2n2次操作把它变成升序。每次操作都可以选一个长度为偶数的连续区间,交换前一半和后一半。输出每次操作选择的区间的第一个和最后一个元素。
思路:
注意紫书上的提示,2n次操作就可以完成了。从头开始遍历序列,属于该位置上的元素,可以在两步之内交换到这里。
#include <bits/stdc++.h> #define inf 0x3f3f3f3f #define MAX 1e3 #define FRE() freopen("in.txt","r",stdin) #define FRO() freopen("out.txt","w",stdout) using namespace std; typedef long long ll; typedef pair<int, int> P; const int maxn = 10010; vector<P> ans; int a[maxn], pos[maxn]; int n; void toSwap(int l, int r) {//完成区间前半段和后半段的交换 int mid = (l + r) / 2; for(int i = 0; l + i <= mid; i++) { swap(a[l + i], a[mid + i + 1]); swap(pos[a[l+i]], pos[a[mid+i+1]]);//记得位置也要跟着交换过来 } ans.push_back(P(l, r)); } int main() { //FRE(); int kase; scanf("%d", &kase); while(kase--) { ans.clear(); memset(a, 0, sizeof(a)); memset(pos, 0, sizeof(pos)); scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); pos[a[i]] = i; } for(int i = 1; i <= n; i++) { //cout<<"GG"<<endl; if(a[i] == i) { continue; } int idx = pos[i]; while(a[i] != i) { if(n - idx + 1 >= idx - i) {//如果idx到n的距离大于等于区间[i,idx]的长度 toSwap(i, i + (idx - i) * 2 - 1); } else {//否则就先将元素往前交换移动 int len = idx - i + 1; if(len % 2 == 1) { toSwap(i+1,idx); }else{ toSwap(i, idx); } idx -= len/2; } } } printf("%d\n",ans.size()); for(int i=0; i<ans.size(); i++){ printf("%d %d\n",ans[i].first,ans[i].second); } } return 0; }