2020牛客暑期多校训练营(第三场)E - Two Matchings(dp)

题目链接: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\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;
}

 

posted on 2020-07-19 22:58  啊啊鄂  阅读(143)  评论(0编辑  收藏  举报

导航