题目链接:Two Matchings

题意:给你一个序列a,你要找到两种完全不同的整个序列的两两匹配,使得所有两两匹配差的绝对值的和最小,输出这个和

思路:对序列a排序后,显然最小的匹配相邻两两匹配,即$(a_2-a_1)+(a_4-a_3)+\dots+(a_n - a_{n-1})$,关键在如何求第二小的匹配

对于第二小的匹配,我们可以讲整个序列分为多个4个数的子序列和多个6个数的子序列

对于4个数的子序列,除了相邻两两匹配外,第二小的匹配为$a_3+a_4-a_2-a_1$

对于6个数的子序列,除了相邻两两匹配外,第二小的匹配为$a_6+a_5+a_3-a_4-a_1-a_2$

对于偶数位进行dp即可,即dp[i]=min(dp[i - 4] + a[i] + a[i - 1] - a[i - 2] - a[i - 3],dp[i - 6] + a[i] + a[i - 1] + a[i - 3] - a[i - 5] - a[i - 4] - a[i - 2])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 200010;
const ll INF = 1000000000000000000;

int T, n;
ll a[N], dp[N];

int main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) dp[i] = INF;
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
sort(a + 1, a + n + 1);
ll res = 0;
for (int i = 1; i <= n; i += 2) res = res + a[i + 1] - a[i];
if (n == 4) {
printf("%lld\n", a[4] + a[3] - a[2] - a[1] + res);
continue;
}
dp[4] = a[4] + a[3] - a[2] - a[1];
dp[6] = a[6] + a[5] + a[3] - a[1] - a[2] - a[4];
for (int i = 8; i <= n; i += 2) {
ll t1 = dp[i - 4] + a[i] + a[i - 1] - a[i - 2] - a[i - 3];
ll t2 = dp[i - 6] + a[i] + a[i - 1] + a[i - 3] - a[i - 5] - a[i - 4] - a[i - 2];
dp[i] = min(t1, t2);
}
printf("%lld\n", dp[n] + res);
}
return 0;
}