题目描述
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:
,其中 ai表示第一列火柴中第 i 个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
输入
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
输出
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。
样例输入
[Sample 1]
4
2 3 1 4
3 2 1 4
[Sample 2]
4
1 3 4 2
1 7 2 4
样例输出
[Sample 1]
1
[Sample 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 ≤火柴高度≤ 2^31 - 1。
题解:
要使得值最小,应当使a盒火柴的第K大与b盒火柴的第K大放在一起。
//数学证明:令a>b,c>d,判断(a-c)^2+(b-d)^2-[(a-d)^2+(b-c)^2]的正负。
因此将两盒火柴都快排一遍可知对应关系;
如果开一个node{ int lenth,ord}来储存高度和原来的位置,那么快排后可知原序列中第几根火柴应该和第几根火柴放在一起。
这时开一个数组 a [ ]来记录对应关系,例如a[i]=j表示第一组火柴的第i根对应第二组火柴第j根。
此时,a[ ]中有多少对逆序数对,就要交换几次。
请看一组样例解释:
先给出火柴高度,[ ]里是在原队列的位置:
8 [1] 6 [2] 9 [3] 2 [4] 4 [5] 5 [6]
1 [1] 5 [2] 2 [3] 8 [4] 6 [5] 3 [6]
将两组火柴排序后得到如下所示(令 最小的情况):
2 [4] 4 [5] 5 [6] 6 [2] 8 [1] 9 [3]
1 [1] 2 [3] 3 [6] 5 [2] 6 [5] 8 [4]
开辟a[ ]来储存原位置对应关系:
a[4]==1 a[5]==3 a[6]==6 a[2]==2 a[1]==5 a[3]==4
整理:
a[1]==5 a[2]==2 a[3]==4 a[4]==1 a[5]==3 a[6]==6
那么为什么所求答案就成了a[ ]的逆序对数目?
原因:
首先答案的情况:
2 [4] 4 [5] 5 [6] 6 [2] 8 [1] 9 [3]
1 [1] 2 [3] 3 [6] 5 [2] 6 [5] 8 [4]
若将第一组火柴按原位置排好,并且令第二组火柴跟着第一组中对应的火柴排好得到:
8 [1] 6 [2] 9 [3] 2 [4] 4 [5] 5 [6]
6 [5] 5 [2] 8 [4] 1 [1] 2 [3] 3 [6]
由此可得 a[1]==5 a[2]==2 a[3]==4 a[4]==1 a[5]==3 a[6]==6
如果要使最小,应该让a[i]==i;
所以要交换火柴序号(在现实中通过交换火柴实现)即a[i]的值使a[i]==i;
交换次数即答案,也是a[ ]的逆序队数目。
求逆序数对可以用树状数组,这里不详讲,也可以用其它方法实现。
见代码:
树状数组版:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct node{
int num,ord;
};
node team1[100010],team2[100010];
int n,a[100010],tree[100010];
int lowbit(int k){
return k&-k;
} //树状数组操作
int scan(){
int x=0;
char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')
x=x*10+c-'0',
c=getchar();
return x;
} //读入优化
void update(int k,int numb){
int i;
for(i=k;i<=n;i+=lowbit(i))
tree[i]+=numb;
return ;
} //树状数组操作
bool comp(node x,node y){
return x.num<=y.num;
}
int get_sum(int k){
int sum=0;
for(int i=k;i>=1;i-=lowbit(i))
sum+=tree[i];
return sum;
}
int main(){
int i,answer=0;
n=scan();
for(i=1;i<=n;i++)
team1[i].num=scan(),
team1[i].ord=i; //位置记录
for(i=1;i<=n;i++)
team2[i].num=scan(),
team2[i].ord=i;
sort(team1+1,team1+1+n,comp);//按高度排序
sort(team2+1,team2+1+n,comp);
for(i=1;i<=n;i++)
a[team1[i].ord]=team2[i].ord; //离散化
for(i=1;i<=n;i++){
update(a[i],1);
answer=(answer+i-get_sum(a[i]))%99999997 ;
} //树状数组求逆序对数目
printf("%d",answer%99999997 );
}
完毕!!!!!