火柴排队题解
题目描述
涵涵有两盒火柴,每盒装有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
样例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
题目分析
贪心方法:将a,b数组排序后,相对应的两个数就是交换后所对应的两个数
证明 (可跳过)
假设a数组中有且两个数为:a1, a2(a1 <= a2) , b数组中有且两个数为:b1, b2(b1 <= b2 <= a1)
则有两种对应方法
a1 -> b1
a2 -> b2
a1 -> b2
a2 -> b1
v
a
l
1
=
(
a
1
−
b
1
)
2
+
(
a
2
−
b
2
)
2
val1 = (a1 - b1) ^ 2 + (a2 - b2) ^ 2
val1=(a1−b1)2+(a2−b2)2
v
a
l
2
=
(
a
1
−
b
2
)
2
+
(
a
2
−
b
1
)
2
val2 = (a1 - b2) ^ 2 + (a2 - b1) ^ 2
val2=(a1−b2)2+(a2−b1)2
因为
b
1
<
=
b
2
b1 <= b2
b1<=b2 所以
(
a
1
−
b
1
)
2
(a1 - b1) ^ 2
(a1−b1)2 >=
(
a
1
−
b
2
)
2
(a1 - b2) ^ 2
(a1−b2)2
若
x
+
y
=
=
a
+
b
,
a
<
=
x
,
y
<
=
b
x + y == a + b, a <= x, y <= b
x+y==a+b,a<=x,y<=b
则
(
x
−
a
)
∗
(
x
+
a
)
<
=
(
b
−
y
)
∗
(
b
+
y
)
(
x
−
1
=
b
−
y
)
(x - a) * (x + a) <= (b - y) * (b + y)(x - 1 = b - y)
(x−a)∗(x+a)<=(b−y)∗(b+y)(x−1=b−y)
所以
x
2
+
y
2
<
=
a
2
+
b
2
x ^ 2 + y ^ 2 <= a ^ 2 + b ^ 2
x2+y2<=a2+b2
所以
v
a
l
1
<
=
v
a
l
2
val1 <= val2
val1<=val2
同理:当 b 1 < = a 1 < = b 2 , a 1 < = b 1 < = b 2 b1 <= a1 <= b2, a1 <= b1 <= b2 b1<=a1<=b2,a1<=b1<=b2 时,都是 v a l 1 < = v a l 2 val1 <= val2 val1<=val2
所以方案1更佳
所以当第i小的数相对应时,距离最小
实现
贪心的思路讲完了,可是怎样交换才能使第i小的数相对应捏???
搜索
分析数据范围,O(n ^ n)的算法都过不了,那就不做吧,所以搜索肯定不行
难点&&重点:
设each[i] = j表示排序后,a[i]与b[j]是相对应的
则b[j]要移到第i号位置
拿样例2举例,则有
each[1] = 1
each[2] = 4
each[3] = 2
each[4] = 3
则用冒泡排序的思想模拟即可(交换相邻的两个数)
关键代码如下
void Bubble_Sort(int arr[]) {
for(int i = n; i >= 2; i--) {
for(int j = 1; j <= i - 1; j++) {
if(arr[j] >= arr[j + 1]) swap(arr[j], arr[j + 1]), ans++;
}
}
}
可冒泡排序的时间复杂度为:O(n ^ n),我们得想更好的办法
观察代码,发现Ta就是一个求逆序对的板子,将Ta换成二路归并排序即可
代码如下
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
const int MO = 99999997;
int n, ans;
int each[MAXN];
struct node {
int index, val;
}a[MAXN], b[MAXN];
bool cmp(node x, node y) {return x.val < y.val;}
void merge_sort(int, int);
int main() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i].val;
a[i].index = i;
}
for(int i = 1; i <= n; i++) {
cin >> b[i].val;
b[i].index = i;
}
sort(a + 1, a + 1 + n, cmp);
sort(b + 1, b + 1 + n, cmp);
for(int i = 1; i <= n; i++) {
each[a[i].index] = b[i].index;
// 或者each[b[i].index] = a[i].index;
}
merge_sort(1, n);
cout << ans;
return 0;
}
void merge_sort(int l, int r) {
if(l == r) return;
int mid = (l + r) / 2;
merge_sort(l, mid);
merge_sort(mid + 1, r);
int i = l, j = mid + 1;
int tem[MAXN], len = l;
while(i <= mid && j <= r) {
if(each[i] < each[j]) {
tem[len++] = each[i++];
}
else {
tem[len++] = each[j++];
ans += mid - i + 1;
ans %= MO;
}
}
while(i <= mid) tem[len++] = each[i++];
while(j <= r) tem[len++] = each[j++];
for(int i = l; i <= r; i++) each[i] = tem[i];
return;
}