「Solution」CF620D Professor GukiZ and Two Arrays

Problem

Link

Professor GukiZ has two arrays of integers, a and b. Professor wants to make the sum of the elements in the array a sa as close as possible to the sum of the elements in the array b sb. So he wants to minimize the value v = |sa - sb|.

In one operation professor can swap some element from the array a and some element from the array b. For example if the array a is [5, 1, 3, 2, 4] and the array b is [3, 3, 2] professor can swap the element 5 from the array a and the element 2 from the array b and get the new array a [2, 1, 3, 2, 4] and the new array b [3, 3, 5].

Professor doesn't want to make more than two swaps. Find the minimal value v and some sequence of no more than two swaps that will lead to the such value v. Professor makes swaps one by one, each new swap he makes with the new arrays a and b.

Input
The first line contains integer n (1 ≤ n ≤ 2000) — the number of elements in the array a.
The second line contains n integers ai ( - 109 ≤ ai ≤ 109) — the elements of the array a.
The third line contains integer m (1 ≤ m ≤ 2000) — the number of elements in the array b.
The fourth line contains m integers bj ( - 109 ≤ bj ≤ 109) — the elements of the array b.

题意:

给定两个数组 \(a,b\) ,可以选择交换 \(a_i,b_j\) 不超过 \(2\) 次,使得 \(| \sum a_i - \sum b_j |\) 最小。

Solution

首先可以发现交换次数 \(k\) 只有 {\(0,1,2\)} 三种可能,考虑分类讨论。

1.\(k=0 \\\)
不交换任何元素,直接将两数组求和计算差的绝对值。

2.\(k=1 \\\)
只交换一次。

可以看到 \(1 \le n,m \le 2000\) ,果断选择暴力枚举 \(a_i,b_j\) 交换的所有情况。

3.\(k=2 \\\)
将交换两次后的式子很显然的变化一下:

\[\begin{aligned} &suma=\sum a_i \\ &sumb=\sum b_j \\ s&=|suma'-sumb'| \\ &=|suma-(a_{i1}+a_{j1})+(b_{i2}+b_{j2})-[sumb-(b_{i2}+b_{j2})+(a_{i1}+a_{j1})]| \\ &=|suma-sumb-2 \times (a_{i1}+a_{j1})+2 \times (b_{i2}+b_{j2})| \end{aligned} \]

所以我们可以先将 \(2 \times (a_{i1}+a_{j1})\) 预处理出来,这个肯定是 \(n^2\) 级别的。

然后枚举 \(2 \times (b_{i2}+b_{j2})\) 也是 \(m^2\) 级别的,所以直接遍历铁不现实,必须采用 \(log\) 级别来搭配。

\(x=suma-sumb+2 \times (b_{i2}+b_{j2}),y=2 \times (a_{i1}+a_{j1})\)

枚举中我们已经确定 \(x\) ,而且要用它减去 \(y\)

所以我们可以二分查找第一个 \(\le x\)\(y\),使得 \(x-y\) 最小且 \(x-y \ge 0\),由于带了绝对值,所以同理还要找第一个 \(\ge x\)\(y\),找到 \(x-y \le 0\)\(|x-y|\) 的最小。

Code

为了方便二分以及存储每对 \(2 \times (a_{i1}+a_{j1})\) 对应的 \(i,j\),将其放入 \(\operatorname{map}\) 。注意一下 \(\operatorname{map}\) 的边界。

#include <map>
#include <cstdio>
#include <algorithm>
#define int long long

using namespace std;

typedef long long LL;
const int Maxn = 2e3;

LL ans;
int n, m, cnt;
int a[Maxn + 5], b[Maxn + 5], c[5];

map < LL , pair < int , int > > mp;

LL Abs (LL x) {
	return x < 0 ? -x : x;
}

LL Min (LL x, LL y) {
	return x < y ? x : y;
}

signed main () {
	scanf ("%lld", &n);
	
	LL suma = 0;
	for (int i = 1; i <= n; i ++) {
		scanf ("%lld", &a[i]);
		suma += a[i];
	}
	
	scanf ("%lld", &m);
	
	LL sumb = 0;
	for (int i = 1; i <= m; i ++) {
		scanf ("%lld", &b[i]);
		sumb += b[i];
	}
	
	//k=0
	ans = Abs (suma - sumb);
	
	//k=1
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			if (ans > Abs (suma - sumb + 2ll * (b[j] - a[i]))) {
				cnt = 1;
				ans = Abs (suma - sumb + 2ll * (b[j] - a[i]));
				c[1] = i, c[2] = j;
			}
		}
	}
	
	//k=2
	for (int i = 1; i < n; i ++) {
		for (int j = i + 1; j <= n; j ++) {
			mp[(a[i] + a[j]) * 2] = make_pair (i, j);
		}
	}
	
	for (int i = 1; i < m; i ++) {
		for (int j = i + 1; j <= m; j ++) {
			LL x = suma - sumb + 2ll * (b[i] + b[j]);
			map < LL , pair < int , int > > :: iterator it = mp.lower_bound (x);
			if (it != mp.end ()) {
				if (ans > Abs (suma - sumb + 2ll * (b[i] + b[j]) - it -> first)) {
					cnt = 2;
					ans = Abs (suma - sumb + 2ll * (b[i] + b[j]) - it -> first);
					c[1] = (it -> second).first, c[2] = i;
					c[3] = (it -> second).second, c[4] = j;
				}
			}
			if (it != mp.begin ()) {
				it --;
				if (ans > Abs (suma - sumb + 2ll * (b[i] + b[j]) - it -> first)) {
					cnt = 2;
					ans = Abs (suma - sumb + 2ll * (b[i] + b[j]) - it -> first);
					c[1] = (it -> second).first, c[2] = i;
					c[3] = (it -> second).second, c[4] = j;
				}
			}
		}
	}
	
	printf ("%lld\n%lld\n", ans, cnt);
	if (cnt == 1) {
		printf ("%lld %lld", c[1], c[2]);
	} else if (cnt == 2) {
		printf ("%lld %lld\n%lld %lld", c[1], c[2], c[3], c[4]);
	}
	return 0;
}

posted @ 2021-08-20 23:42  PoisonNNN  阅读(65)  评论(1编辑  收藏  举报