火柴排队题解

题目

题目描述

涵涵有两盒火柴,每盒装有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=(a1b1)2+(a2b2)2
v a l 2 = ( a 1 − b 2 ) 2 + ( a 2 − b 1 ) 2 val2 = (a1 - b2) ^ 2 + (a2 - b1) ^ 2 val2=(a1b2)2+(a2b1)2
因为 b 1 < = b 2 b1 <= b2 b1<=b2 所以 ( a 1 − b 1 ) 2 (a1 - b1) ^ 2 (a1b1)2 >= ( a 1 − b 2 ) 2 (a1 - b2) ^ 2 (a1b2)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) (xa)(x+a)<=(by)(b+y)x1=by
所以 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;
}
posted @ 2020-07-25 21:26  C2022lihan  阅读(81)  评论(0编辑  收藏  举报