牛客暑期多校第三场 E-Two Matchings

Two Matchings

https://ac.nowcoder.com/acm/contest/5668/E

E-Two Mathcings

题意

给一个序列
要找到两种不同的整个序列的两两匹配,使得所有两两匹配的差的和最小,输出这个和。

思路

经过几次思考和画画发现,其实最佳的匹配策略只有把原来的序列升序排序后,分为长度为4的块和长度为6的块,每个块内部做两种不同的排列,才能使得总cost最小(正确性待证)

那么,对长度为4的块,两种匹配方法为 1-2 3-4,1-4,2-3;对长度为6的块,两种匹配的方法为1-2 3-4 5-6,1-3 2-5 4-6。(匹配方法或许不唯一,但懒得再举例。。

可以发现cost最小的匹配满足:总花费 =

回到原来的序列,因为n为偶数,假设原来的整个序列为一起进行两两匹配,那它原始的花费在理想情况下应该也为

但是由于我们发现可以把原序列长度拆成,即a个长度为4的区间和b个长度为6的区间。而相比原始的cost,减少的就是每个区间裂开的点的值。那我们用dp数组状态转移一下:

最终答案就是

排个序,转移下状态输出结果。

代码

/*
* @ author: dragon_bra
* @ email: tommy514@foxmail.com
* @ data: 2020-07-18 12:51
*/

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <vector>

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-5;
const int N = 2e5 + 10;

int T;
int n;
ll a[N], dp[N];
ll ans = 0;

int main() {
    cin >> T;
    while (T--) {
        scanf("%d", &n);
        ll mx = 0, mi = 1e9 + 10;
        for (int i=1; i<=n; i++) {
            scanf("%lld", &a[i]);
            mx = max (mx, a[i]);
            mi = min (mi, a[i]);
        }
        sort(a+1, a+n+1);
        ans = (mx-mi) * 2;

        dp[0] = 0;
        for (int i=1; i<=n; i++) dp[i] = dp[i-1];

        ll cmp = 0;
        for (int i=1; i<=n; i++) {
            dp[i] = max(dp[i-1], dp[i]);
            if ((i-1)%2 == 0) {
                if (i>=4 && (n-i+1)>=4) {
                    dp[i] = max (dp[i], dp[i-4] + a[i] - a[i-1]);
                }
                if (i>=6 && (n-i+1)>=4) {
                    dp[i] = max (dp[i], dp[i-6] + a[i] - a[i-1]);
                }
            }
        }

        printf("%lld\n", ans-dp[n]*2);
    }

    return 0;
}
全部评论
人上人 人上人
点赞 回复
分享
发布于 2020-07-18 20:58

相关推荐

1 收藏 评论
分享
牛客网
牛客企业服务