[7/18牛客暑期多校第三场] E.Two Matchings

题意

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

思路

经过几次思考和画画发现,其实最佳的匹配策略只有把原来的序列升序排序后,分为长度为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最小的匹配满足:总花费 = ( m a x ( a i ) − m i n ( a i ) ) ∗ 2 (max(ai) - min(a_i) ) * 2 (max(ai)min(ai))2

回到原来的序列,因为n为偶数,假设原来的整个序列为一起进行两两匹配,那它原始的花费在理想情况下应该也为 ( m a x ( a i ) − m i n ( a i ) ) ∗ 2 (max(ai) - min(a_i) ) * 2 (max(ai)min(ai))2

但是由于我们发现可以把原序列长度拆成 2 k = 4 a + 6 b 2k = 4a + 6b 2k=4a+6b,即a个长度为4的区间和b个长度为6的区间。而相比原始的cost,减少的就是每个区间裂开的点的值。那我们用dp数组状态转移一下: d p [ i ] = m a x ( d p [ i − 4 ] + 裂 开 [ i − 1 , i ] , d p [ i − 6 ] + 裂 开 [ i − 1 , i ] , d p [ i − 1 ] ) dp[i] = max(dp[i-4] + 裂开[i-1,i], dp[i-6] + 裂开[i-1,i], dp[i-1]) dp[i]=max(dp[i4]+[i1,i],dp[i6]+[i1,i],dp[i1])

最终答案就是 ( m a x ( a i ) − m i n ( a i ) ) ∗ 2 − d p [ n ] ∗ 2 (max(ai) - min(a_i) ) * 2 - dp[n]*2 (max(ai)min(ai))2dp[n]2

O ( n l o g n ) O(nlogn) O(nlogn)排个序, O ( n ) O(n) O(n)转移下状态输出结果。

代码

/*
* @ 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值