NOIP2013 Day1 T2 火柴排队(归并排序+离散化)

题面:

                                                         火柴排队

                                                            (match.cpp/c/pas)
【问题描述】
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各
自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑ni=1(?? − ??)2,其
中 ai表示第一列火柴中第 i 个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最
小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最
小交换次数对 99,999,997 取模的结果。
全国信息学奥林匹克联赛(NOIP2013)复赛 提高组 day1
第 3 页共 4 页
【输入】
输入文件为 match.in。
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
【输出】
输出文件为 match.out。
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。
【输入输出样例 1】
match.in
4
2 3 1 4
3 2 1 4

match.out
1
【输入输出样例说明】
最小距离是 0,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的
前 2 根火柴。
【输入输出样例 2】
match.in
4
1 3 4 2
1 7 2 4

match.out
2
【输入输出样例说明】
最小距离是 10,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交
换第 2 列中后 2 根火柴的位置。
【数据范围】
对于 10%的数据, 1 ≤ n ≤ 10;
对于 30%的数据,1 ≤ n ≤ 100;
对于 60%的数据,1 ≤ n ≤ 1,000;
对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 231-1

  这道题,我第一眼还以为是计算一个数组,看每个数需要调整的位置,然后用线段树维护一下……(捂脸)等我做了40min,才反应过来是重新定义一个顺序,之后求逆序对就好。

  思路:

  把两个队列离散化,如果不懂得同学可以看一下,懂得直接跳过就好。

  我们现将两个队列中的每个数变成该数在队列中的顺序。举个例子:

  如果原队列是1 7 2 4 5 8

  那么改后就是1 5 2 3 4 6

  这样做的原因是火柴的高度太大,数组开不下,而实际也就只有n个数,所以把每个数改成在原序列的顺序就好。(其实就是数列改序列)

  离散化部分结束。

  之后再把第一队的顺序定义成标准序列,把第二队在标准序列中对应的位置改出来就好。

  拿样例二举例:(直接用离散化后的队列)

  队列一:1 3 4 2

  队列二:1 4 2 3

  则标准序列:1 3 4 2(也就是说正确顺序)

  而队列二为:1 4 2 3

  那么就有4 3,4 2这两对逆序对。(即2 4,3 4的顺序反了,需要互换,与题意刚好吻合,并且要移几位,就有几对逆序对)

  但我们如果用离散化过的队列二来找逆序对,很明显是不合理的,因为大小关系不一定。

  那我们可以换个想法,用标准序列处理一下队列二。

  离散化后的i这个数在标准序列中的位置(也可以理解为大小)是s[i]。(此处s数组为新建的,目的就是村i这个数的位置。)

  也就是把原来无序的序列变成升序的。

  则标准队列不变:1 3 4 2

  而队列二根据标准队列变为:1 3 4 2

  (实在不懂得这样想:1是第一大的,3是第二大的,4是第三大的,2是第四大的,那么1对应第1位,3对应第2位,4对应第3位,2对应第4位。这样处理后再把第二队的1 4 2 3改变,1是1,4是3,2是4,3是2。这里都是1 3 4 2只是巧合罢了)

  很简单就可以看出有3 2,4 2这两对逆序对。

  这样处理完成,就是一个很正常的求逆序对了,用归并排序或者树状数组就好。(我这里用的是归并排序)

  代码如下:

 

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

#define FOR(l,r) for(int i=l;i<=r;++i)

const int N=100010;

int ans,n,b[N],c[N],s[N],d[N],p[N];
//b数组存第二队离散化的结果,c数组存第二队离散化的结果。
//s数组存i这个数代表的值。即:i这个数在标准序列中的位置是s[i]。 
//d数组存将第二队按我们的标准顺序排列后的结果。 
struct node{
    int va,num;
}a[N];//a数组是辅助离散化的,va代表值,num代表在原序列的编号。 

template<class T>inline void read(T &x)//读入优化。 
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
    while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    x=f ? -x:x;
    return;
}

bool cmp(node x,node y)//升序排列(其实是我忘了sort的默认排序方式……)。 
{ return x.va<y.va; } 

void Merge_Sort(int l,int r)//归并排序求逆序对。(NOIP2017的初赛有) 
{
    if(l==r)return;
    int mid=(l+r)>>1;
    Merge_Sort(l,mid);
    Merge_Sort(mid+1,r);
    int ll=l,rr=mid+1,k=l;
    while(ll<=mid&&rr<=r)
    {
        if(d[ll]>d[rr])
        {
            p[k]=d[rr];
            ans+=mid-ll+1;
            ++k;
            ++rr;
        }
        else
        {
            p[k]=d[ll];
            ++k;
            ++ll;
        }
    }
    while(ll<=mid){ p[k++]=d[ll]; ++ll; }
    while(rr<=r){ p[k++]=d[rr]; ++rr; }
    FOR(l,r)d[i]=p[i];
    return;
}

int main()
{
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    read(n);
    FOR(1,n){ read(a[i].va); a[i].num=i; }
    sort(a+1,a+n+1,cmp);
    FOR(1,n)b[a[i].num]=i;//离散化。 
    FOR(1,n)s[b[i]]=i;//记录b[i]这个数在原序列的位置。 
    FOR(1,n){ read(a[i].va); a[i].num=i; }
    sort(a+1,a+n+1,cmp);
    FOR(1,n)c[a[i].num]=i;//离散化。 
    FOR(1,n)d[i]=s[c[i]];//将第二队按照标准序列排序。 
    Merge_Sort(1,n);
    printf("%d",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

 

转载于:https://www.cnblogs.com/Wujiga/p/7741694.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值