[Cogs]-1032-最小交换排序

本文介绍了一种解决最小交换排序问题的独特方法,通过离散化和策略调整,实现O(n)复杂度的高效算法。该方法巧妙地将原始序列转化为已排序序列,进而计算所需的最少交换次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

奇♂怪的题,我用了丧心病狂的方法,有必要写下题解

最小交换排序

【问题描述】

    小x最近切了一道很水的排序题,但是在AC之后,他想到了一个问题:

给定n个不相等的整数,将其按从小到大排序,你每次可以对任意一对数进行交换,问最少的交换次数是多少?

【输入】

第一行:一个整数n

第二行:n个用空格隔开的正整数ai,-2^31<ai<2^31

【输出】

一个整数,表示最小交换次数。

【输入输出样例】

minsort.in minsort.out
88 23 4 16 77 -5 53 100  5

【数据范围】

   20%   n<=100

   50%   n<=5000

   100%  n<=100000

 

根据dw定理可以推出答案的值就是最长上升子序列的长度(参见我写的偏序集Dilworth定理证明及应用

但说是丧心病狂,我们就有丧心病狂的方法,最长上升子序列O(n*logn)但我们用O(n)的方法,并且常数还比较小

先把{ai}离散化到{bi}

比如我们输入的{ai}是 2  9  18  36  7

{bi}:1  3  4  5  2

答案等价于我们把bi变成1 2 3 4 5   也就是b[i] = i(废话

这里我们的策略是到第i位时,把实际在第i位的数b[i]和应该在第i位的数i调换位置(当然了di == i时不用换了

那么问题就来了,挖掘机技术哪家强?

好吧,问题是我们怎么知道要和bi交换的i在哪里?

我们再来一个数组{fi}保存{bi}中数字i的位置,做出{fi}的原理也和{ai}变到{bi}一样离散一下(就是不用排序

我们就把b[i]和b[f[i]]交换位置就行了

但是交换了还得再调整部分fi的值

b[f[i]](数值为i)上的值变成了b[i]所以f[b[i]](b[i]的位置改了所以f[b[i]]要改)要改,改成什么呢?

b[i]是和i(b[f[i]])交换的,所以b[i]的位置要变成原来i的位置,i的位置又是f[i],所以f[b[i]]改成f[i]

 

还要变的是f[i],b[i]和i换了,所以i的位置就是i,也就是f[i] = i;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#include<queue>
#define maxn 100010
using namespace std;
struct node
{
    int x, num;
} a[maxn];
bool cmp(node a, node b)
{
    return a.x < b.x;
}
int b[maxn], f[maxn], ans, n;
inline void solve()
{
    scanf(“%d”, &n);
    for (int i = 0; i < n; i++)
    {
        scanf(“%d”, &a[i].x);
        a[i].num = i;
    }
    sort(a, a + n, cmp);
    for (int i = 0; i < n; i++)
        b[a[i].num] = i;
    for (int i = 0; i < n; i++)
        f[b[i]] = i;
    int t;
    for (int i = 0; i < n; i++)
    {
        if (f[i] != i)
        {
            f[b[i]] = f[i];
            t = b[i];
            b[i] = b[f[i]];
            b[f[i]] = t;
            f[i] = i;
            ans ++;
        }
    }
    return ;
}
int main()
{
    freopen(“minsort.in”, “r”, stdin);
    freopen(“minsort.out”, “w”, stdout);
    solve();
    printf(“%d”, ans);
    return 0;
}
http://www.laomaotao.net/?E3887

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值