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个序列求逆序对。
代码
#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;
}

本文介绍了一道名为“火柴排队”的算法题,探讨了如何通过交换火柴位置使两列火柴间的距离最小,同时给出了求解最少交换次数的方法,并详细解释了解题思路与步骤。
&spm=1001.2101.3001.5002&articleId=79652659&d=1&t=3&u=41836ebf58af4e19bf98b97e3c2ab9e3)
549

被折叠的 条评论
为什么被折叠?



