奇♂怪的题,我用了丧心病狂的方法,有必要写下题解
最小交换排序
【问题描述】
小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