UVa 1611 - Crane
链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4486
题意:
输入一个1~n(1≤n≤10000)的排列,用不超过9e6次操作把它变成升序。
每次操作都可以选一个长度为偶数的连续区间,交换前一半和后一半。
例如,输入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即可。
分析:
构造法。
依次把1~n安排到正确的位置。可以发现每个数字最多变动两次,故2n次操作就足够了。
代码:
1 #include <cstdio> 2 #include <vector> 3 using namespace std; 4 5 const int UP = 10000 + 5; 6 int a[UP], p[UP]; // p[i]记录了数字i的当前位置 7 vector<int> ansL, ansR; 8 9 void change(int L, int R){ 10 ansL.push_back(L); ansR.push_back(R); 11 int M = L + (R - L + 1) / 2; 12 for(int i = 0; i < M - L; i++){ 13 swap(a[L+i], a[M+i]); 14 p[a[L+i]] = L + i; 15 p[a[M+i]] = M + i; 16 } 17 } 18 19 int main(){ 20 int T, n; 21 scanf("%d", &T); 22 while(T--){ 23 scanf("%d", &n); 24 for(int i = 1; i <= n; i++){ 25 scanf("%d", &a[i]); 26 p[a[i]] = i; 27 } 28 29 ansL.clear(); ansR.clear(); 30 for(int i = 1; i <= n; i++){ 31 if(p[i] == i) continue; 32 if(2*p[i] > n+i+1) change(i+(p[i]-i+1)%2, p[i]); 33 change(i, 2 * p[i] - i - 1); 34 } 35 36 printf("%d\n", ansL.size()); 37 for(int i = 0; i < ansL.size(); i++) 38 printf("%d %d\n", ansL[i], ansR[i]); 39 } 40 return 0; 41 }