2016暑期集训16B手套

来源:互联网 发布:asi算法理解 编辑:程序博客网 时间:2024/06/11 16:31

手套

时间限制: 1 Sec 内存限制: 128 MB
提交: 9 解决: 7
[提交][状态][讨论版]
题目描述
你现在有N对手套,但是你不小心把它们弄乱了,需要把它们整理一下。N对手套被一字排开,每只手套都有一个颜色,被记为0~N-1,你打算通过交换把每对手套都排在一起。由于手套比较多,你每次只能交换相邻两个手套。请你计算最少要交换几次才能把手套排整齐。

输入
输入第一行一个N,表示手套对数。

第二行有2N个整数,描述了手套的颜色。每个数都在0~N-1之间,且每个数字都会出现恰好两次。

输出
一行,包含一个数,表示最少交换次数。

样例输入
2
0 1 0 1
样例输出
1
提示
30%的数据N≤9;

60%的数据N≤1000;

100%的数据N≤200,000。
这题在考试中把我成功卡死,
我甚至开始想这个在生活中也的确有用。
也想过贪心,但我就是觉得不可能,实际上是对的!!!
贪心后就是逆序对。

30% 搜索
60% 可以采取如下贪心策略:从左到右扫描手套的序列,若当前手套的颜色为x,则找到颜色x的另一只手套(很显然在右边),将它向左交换至第一只旁边,继续扫描。
100% 按上述贪心做法,我们可以在O(N)的时间内确定最后的颜色序列。相同颜色的手套之间必然不会发生交换,于是我们就可以确定每只手套的最终位置了。问题
转化为:给出一个序列,其中的元素各不相同,求最少交换几次能变成另一个序列。将序列的元素置换一下后,这个问题可以进一步转化为最少交换几次可以将数列排好序,即求一个数列的逆序对个数,可以用归并排序或者数据结构(线段树、树状数组等)解决。

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#define ll long longusing namespace std;int n,a[400005],c[400005],b[400005];int d[400005],e[400005];ll ans;void gb(int l,int r){    if (l>=r) return;    int mid=(l+r)>>1;    gb(l,mid);    gb(mid+1,r);    int st=l,ed=mid+1,cnt=l;    while (st<=mid && ed<=r)    {        if (a[st]<=a[ed])        {            c[cnt]=a[st];            cnt++;            st++;        }        else        {            ans=ans+mid-st+1;            c[cnt]=a[ed];            cnt++;            ed++;        }    }    while (st<=mid)    {        c[cnt]=a[st];        cnt++;        st++;    }    while (ed<=r)    {        c[cnt]=a[ed];        cnt++;        ed++;    }    for (int i=l;i<=r;i++)        a[i]=c[i];}int main(){    scanf("%d",&n);    int tmp=0;    for (int i=1;i<=2*n;i++)    {        scanf("%d",&a[i]);        if (!b[a[i]])        {            b[a[i]]=1;            d[++tmp]=a[i];            d[++tmp]=a[i];        }    }    for (int i=0;i<n;i++)    {        e[d[2*i+1]]=i;    }    for (int i=1;i<=2*n;i++)    {        a[i]=e[a[i]];    }    gb(1,2*n);    cout<<ans<<endl;    return 0;}
0 0