您的位置:首页 > 其它

【noip 2013】火柴排队

2017-10-25 11:02 232 查看
去题面的传送门

QAQ昨天队内胡策的T1,成功打次了

没做过火柴排队的我考完试先跑过来做这道题了

首先,对于两个序列,要使他们之间的距离最小,也就是Σ(ai+bi)^2最小,肯定是最大的和最大的对应,次大的和次大的对应,也就是说,把两个序列排序后,各个位上一一对应。但是还要保证交换次数最少,所以不能打乱顺序。

举个栗子:

A序列:4 7 2 1

B序列:3 2 1 4

排序后:

A:1 2 4 7

B:1 2 3 4

所以我们得到1,1对应,2,2对应,4,3对应,7,4对应,所得距离最小

对于原序列,我们把每个数字排序后的序号标记在对应的原位置上

也就是:

A:3 4 2 1

B:3 2 1 4

现在问题转化为,如何交换B序列中的相邻数字,使用最少的步数来使B变为A

现在,我用C数组表示,如果要使B交换后和A相同,各个数字应该放在哪个位置上

C:1 3 4 2

(当然,C数组的统计,还要用一个类似于模拟map函数的数组实现)

也就是说交换完成后的B序列,对应的C应该是1 2 3 4的严格递增1的序列

所以我们要用最少的交换次数,来使C变为一个严格上升的序列,如何求最少交换次数?

求逆序对个数

证明:

对于一个严格递增的序列,一定不存在逆序对。对于一个非严格递增的序列,一定存在相邻的数字为一对逆序对,所以,我们的最小交换次数就是逆序对的个数。

我用的归并排序,之前有归并排序求逆序对的博客

PS:数组模拟map求C数组的时候有点晕,一定要仔细想想

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=100000+10,mo=99999997;
int n,ans;
int c[maxn],fin[maxn],m[maxn];
struct hh
{
int num,x;
}a[maxn],b[maxn];

bool cmp(hh x,hh y)
{
return x.x<y.x;
}
void merge_sort(int l,int r,int mid)
{
int i=l,j=mid+1;
int k=0;
while(i<=mid&&j<=r)
{
if(c[i]>c[j])
{
m[++k]=c[j++];
ans=(ans+mid-i+1)%mo;
}
else
{
m[++k]=c[i];
i++;
}
}
while(i<=mid) m[++k]=c[i++];
while(j<=r) m[++k]=c[j++];
for(int t=l;t<=r;++t)
c[t]=m[t-l+1];
}
void done(int l,int r)
{
if(r>l)
{
int mid=(l+r)>>1;
done(l,mid);
done(mid+1,r);
merge_sort(l,r,mid);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i].x);
a[i].num=i;
}
for(int i=1;i<=n;++i)
{
scanf("%d",&b[i].x);
b[i].num=i;
}
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;++i) fin[i]=a[i].num;
for(int i=1;i<=n;++i) c[b[i].num]=fin[i];
done(1,n);

printf("%d",ans%mo);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: