UVa 1611 起重机(Crane)

题意:
输入一个1~n的排列,用不超过9^6的操作把它变成升序。每次操作都可以选一个长度为偶数的连续区间,交换前一半和后一半。

思路:
俺们发现 例如按这个测试样例
5 4 6 3 2 1
我们要先把1移到第一个位置,然后就不管了,注意到1移到第一个位置后,不必再去动
无论如何,第一步将1移到中间偏左的位置
=> 3 2 1 5 4 6
然后从 1需要移动到的位置延伸到1的距离为 3 - 1 = 2
然后取 3 2 和 1 5交换位置
=> 1 5 3 2 4 6
同样的,将2移到中间偏左的位置,(注意这里的中间已经省去第一个位置了)
=>.1 5 2 3 4 6
然后取 2需要移动到的位置延伸到 2 的距离为1
然后取 5 2 交换位置
=> 1 2 5 3 4 6
细心的同学可能发现了 我也可以直接 1 5 3 2 4 6 直接变成 1 2 4 5 3 6 当然可以,但是为了简化代码,所以合并在一起

那么为什么总要移动到中间偏左的位置呢? 因为这样一定可以找到一个延伸距离,使数值从自身开始往后延伸不会碰到越界的情况
例如 3 2 4 5 1 6 比如我们要将1移到第一个位置,注意到我们可以移动 3 2 4 5 和 1 6 ? ? ,因此只要后面的距离够长,总能一步到位
所以
step 1 创建一个够长的后面距离
step 2 移动

这样就是LRJ 所说的2 * n的时间
这个方法要注意有一个BUG,在代码里面已经特判了。

#include<bits/stdc++.h>
#define LL long long
#define ms(s) memset(s, 0, sizeof(s))
using namespace std;
const int maxn = 1e4 + 10;

vector<pair<int, int>> vec;
int a[maxn];
int p[maxn];

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while(T--) {
        vec.clear();
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
            p[a[i]] = i;
        }
        for(int i = 1; i <= n; i++) {
            if(i == a[i]) continue;
            int pos = p[i];
            //step 1
            int mid = (i + pos) / 2;
            int len = mid - i + 1;
            if((i + pos) % 2 == 0) len--; 
            vec.push_back(make_pair(pos - 2 * len + 1,pos));
            if(pos - 2 * len + 1 - pos != 1) {
                for(int j = pos - 2 * len + 1; j <= mid; j++) {
                    swap(a[j], a[j + len]);
                    p[a[j]] = j;
                    p[a[j + len]] = j + len;
                }
            } else {
                swap(a[pos - 2 * len + 1], a[pos]);
                p[a[pos - 2 * len + 1]] = pos - 2 * len + 1;
                p[a[pos]] = pos;
            }
            if(i == a[i]) continue;
            len = p[i] - i;
            vec.push_back(make_pair(i, i + 2 * len - 1));
            for(int j = i; j < i + len; j++) {
                swap(a[j], a[j + len]);
                p[a[j]] = j;
                p[a[j + len]] = j + len;
            }
        }
        cout << vec.size() << endl;
        for(auto& pair: vec) {
            cout << pair.first << " " << pair.second << endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值