题目链接: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])
#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 ", 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 ", dp[n] + res); } return 0; }