UVA - 1611 Crane (思路题)

题目:

输入一个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;
}

猜你喜欢

转载自www.cnblogs.com/sykline/p/10347621.html