该题刘汝佳给了提示:2n次操作就足够了 。 为什么呢?
题目只允许一种操作:选一个长度为偶数的连续区间,交换前一半和后一半 。 对待区间排序问题,贪心法通常怎么用? 对,还记得这章习题《外星人聚会》吗 ? 我们贪心的将当前位置的数安置好就可以了 。那么这样最多需要多少次操作呢? 就是2n次。 对于每一个数,我们想把它安排到它应该在的位置,最多只要2次操作 。
那么我们就可以分情况讨论:
1、如果i - 1 + (pos[i] - i)*2 <= n (pos[i]表示数字i所在的位置,i为第i位) 那么我们只要用一次操作就可以了(想想为什么,想想这个交换操作的定义)。
2、如果条件不成立,那么我们为了将i移到i位,我们所做的操作必须包含i ,但是操作要求区间长度必须为偶数,所以要判断一下j - i + 1的奇偶,如果为偶数,那么我们直接对区间[i,j]进行操作, 然后条件1就一定成立了,因为将足够的数移到了i的后面 。 如果j - i + 1为奇数,我们就交换区间[i+1,j] ,然后条件一同样会成立 。
所以对于每个数将它安置好最多需要2次操作,总共最多2n次 。
细节参见代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 10000 + 10; int T,n,a[maxn],pos[maxn]; struct node{ int l,r; }pre[531441]; void exchange(int i,int j) { int len = (j - i + 1)/2; for(int k=1;k<=len;k++) { swap(a[i-1+k],a[i-1+k+len]); swap(pos[a[i-1+k]],pos[a[i-1+k+len]]); } } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pos[a[i]] = i; } int cnt = 0; for(int i=1;i<=n;i++) { if(i == pos[i]) continue; if(i + (pos[i] - i)*2 - 1 <= n) { pre[cnt].l=i; pre[cnt++].r=i + (pos[i] - i)*2 - 1; exchange(i,i + (pos[i] - i)*2 - 1); } else { int j = pos[i],len = j-i+1; if(len%2 == 0) { pre[cnt].l=i; pre[cnt++].r=j; exchange(i,j); i--; } else { pre[cnt].l=i+1; pre[cnt++].r=j; exchange(i+1,j); i--; } } } printf("%d\n",cnt); for(int i=0;i<cnt;i++) printf("%d %d\n",pre[i].l,pre[i].r); } return 0; }