UVA 1611 Crane 起重机 (子问题)

题意:给一个1~n排列,1<=n<=10000,每次操作选取一个长度为偶数的连续区间。交换前一半和后一半,使它变成升序。

题解:每次只要把最小的移动到最左边,那么问题规模就缩小了。假设当前区间为[l,r],不难发现,只要最小的数字在[l,l+(r+1-l)/2]这个区间内,一定可以通过一次交换把最小的数字移动到l处,否则,先一定可以一次交换把最小的数字移动到上述区间中。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e4+5;
int a[maxn],pos[maxn];

inline void exchange(int s1,int s2)
{
    int len = s2 -s1;
    for(int i = s1, maxi = s1+len; i < maxi; i++){
        swap(pos[a[i]],pos[a[i+len]]);
        swap(a[i],a[i+len]);
    }
}

int L[maxn<<1],R[maxn<<1];

#define OUT(a,b)\
printf("%d %d\n",a,b)

#define ADDANS(l,r) L[cnt] = l;R[cnt++] = r

int main()
{
    //freopen("in.txt","r",stdin);
    int T; scanf("%d",&T);
    while(T--){
        int n; 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,tmp = n+1; i < n; i++ )if(pos[i]!=i) {
            if( pos[i]<<1 > tmp+i ){
                int len = tmp - i;
                if(len&1){
                    ADDANS(i+1,n);
                    exchange(i+1,i+1+(len>>1));
                }else {
                    ADDANS(i,n);
                    exchange(i,i+(len>>1));
                }
            }
            ADDANS(i,(pos[i]<<1)-i-1);
            exchange(i,pos[i]);
        }
        printf("%d\n",cnt);
        for(int i = 0; i < cnt; i++){
            OUT(L[i],R[i]);
        }
    }
    return 0;
}

 

posted @ 2015-08-03 20:41  陈瑞宇  阅读(331)  评论(0编辑  收藏  举报