ybt1876 火柴排队(NOIP2013提高组第2题)

本文介绍了一道名为“火柴排队”的算法题,探讨了如何通过交换火柴位置使两列火柴间的距离最小,同时给出了求解最少交换次数的方法,并详细解释了解题思路与步骤。

ybt1876  火柴排队(NOIP2013提高组第2题)

时空限制    1000ms/128MB

题目描述

        涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:∑(ai−bi)^2

        其中ai 表示第一列火柴中第i个火柴的高度,bi表示第二列火柴中第 i个火柴的高度。

        每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997取模的结果。

输入

        共三行,第一行包含一个整数n,表示每盒中火柴的数目。

        第二行有 n个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。

        第三行有 n个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

输出

        一个整数,表示最少交换次数对 99,999,997 取模的结果。

输入样例1

4
2 3 1 4
3 2 1 4

输出样例1

1

输入样例2

4
1 3 4 2
1 7 2 4

输出样例2

2

说明/提示

【输入输出样例说明1】

最小距离是0 ,最少需要交换 1 次,比如:交换第 1列的前2 根火柴或者交换第 2列的前 2根火柴。

【输入输出样例说明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≤火柴高度≤maxlongint

分析

对于两个序列:

序列1:a1 a2 a3 … an

序列2:b1 b2 b3… bn

最少需要交换(只能相邻交换)多少次,距离最小。

两个序列距离定义为:∑(ai​−bi​) 2

首先要确定什么时候距离最小。可以证明,当a1<a2<a3<…<an且b1<b2<b3<…<bn时,距离最小。如果a1、a2、a3…an不满足上面的单调关系,我们只需将序列2中相应大小的数据和它对应时,这时距离可取得最小值。因此本题就转化为求将序列2中相对应大小的数据移到和1对应位置的次数。比如:

9 8 6 7 4

7 8 5 2 3

先标出大小关系,括号内为从小到大编号:

序列1:9(5) 8(4) 6(2) 7(3) 4(1)

序列2:7(4) 8(5) 5(3) 2(1) 3(2)

那么只需将第2个序列调整成下面序列3即可:

序列3:8(5) 7(4) 3(2) 5(3)  2(1)

这时,序列1和序列3的距离最小。

实际上,对于所有的大小关系满足序列1、序列2的数据,移动次数是一样的(虽然最小距离可能不一样,但那不是我们所关心的,我们只需要求最小移动次数)。基于此,我们可以对上述数据进一步做转换。

将两个序列转换成相对应大小顺序编号(不妨从小到大):

序列1’:5  4 2  3 1

序列2’:4 5 3 1 2

到目前为止,问题已经转化成将序列2’转换成序列1’的最小移动次数(只能相邻位置交换)。

观察这两个序列,可以注意到,它一个是1-n的一个排列(这也是从前面转化到这一步的目的)。那么我们对两个序列采用同样的编码(就是将两个序列中同一个数字用另外一个数字替换),那么新的最小移动次数就是我们需要求的。编码的目的是什么?当然是为了让第一个序列有序。那么编码方式如下:

5用1表示,4用2表示,2用3表示,3用4表示,1用5表示

这样新的两个序列为:

序列1’’:1 2 3 4 5

序列2’’: 2 1  4 5 3

这样问题就转换成了序列2’’转换成序列1’’的最小移动次数。

这不正是求序列2’’逆序对数目吗?

解题过程:

  1. 将两个序列用大小序号替换(通过排序取得大小序号)
  2. 将两个新序列编码(第1个新序列原定编码规则)
  3. 对编码后的第2个序列求逆序对。

代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100005, MOD = 99999997;
struct node{
	int data,id;
	bool operator < (const node &x)const{
		return data<x.data;
	}
}t[N];
int n,a[N],b[N],c[N],f[N];
long long ans=0;

void mergesort(int L,int R){
	if (L>=R) return;
	int mid=(L+R)/2;
	mergesort(L,mid);
	mergesort(mid+1,R);
	int i=L,j=mid+1,k=L;
	while (i<=mid && j<=R)
		if (c[i]<=c[j]) b[k++]=c[i++];
		else b[k++]=c[j++],ans=(ans+mid-i+1)%MOD;
	while (i<=mid) b[k++]=c[i++];
	while (j<=R) b[k++]=c[j++];
	for (i=L; i<=R; ++i) c[i]=b[i];
}

int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for (int i=1; i<=n; ++i){
		cin>>t[i].data;
		t[i].id = i;	//存原序列中位置
	}
	sort(t+1,t+n+1);	//对第一个序列按数据递增排序
	for (int i=1; i<=n; ++i) a[t[i].id]=i;	//a数组存序列1数据的大小序号
	for (int i=1; i<=n; ++i){
		cin>>t[i].data;
		t[i].id = i;
	}
	sort(t+1,t+n+1);
	for (int i=1; i<=n; ++i) b[t[i].id]=i;	//b数组存序列2数据的大小序号
	for (int i=1; i<=n; ++i) f[a[i]]=i;		//由新序列a数组确定编码规则
	for (int i=1; i<=n; ++i) c[i]=f[b[i]];	//将b数组重新编码后保存至c数组
	mergesort(1,n);//对c数组求逆序对后输出
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值