2020牛客暑期多校第三场E-Two Matchings(规律DP)

题目大意:给你一个数列,从$a_1->a_n$,你需要找到两个匹配序列$p,q$其中$p_i\neq q_i$,使得$(\sum_{i=1}^{n}abs(a_i-a_{p_i}))/2+(\sum_{i=1}^{n}abs(a_i-a_{q_i}))/2$最小,问这个最小值是多少。n保证为偶数,sum(n)<=2e5

输入

2
4
0 8 0 0
6
3 1 4 1 5 9

输出

16
16

如果只是找一个序列的话很好找,我们将a排序,则可得1 2 3 4 5 6这样的有序数列,直接相邻两个相减就是最小值了。我们可以保留这个最小值,接下来我们找次小值,那么就是将数列向前移动一格,第一个数到末尾,然后相邻两个相减,那么这个是次优的。

但很明显,这样做是有限制的,而显然,对于长度为4的序列,这个规律也成立。对于长度为8的序列,他可以拆成两个长度为4的序列,在这之后的数就可以由多个长度为4的和6的拼接而成。那么也就是说我们可以用DP来维护这个次小值。

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mac=2e5+10;

int a[mac];
ll dp[mac];

int main(int argc, char const *argv[])
{
    int t;
    scanf ("%d",&t);
    while (t--){
        int n;
        scanf ("%d",&n);
        for (int i=1; i<=n; i++)
            scanf ("%d",&a[i]);
        sort(a+1,a+1+n);
        ll ans=0;
        for (int i=1; i<=n; i+=2)
            ans+=a[i+1]-a[i];
        if (n>=4) dp[4]=a[3]-a[2]+a[4]-a[1];
        if (n>=6) dp[6]=a[3]-a[2]+a[5]-a[4]+a[6]-a[1];
        if (n>=8) dp[8]=a[3]-a[2]+a[4]-a[1]+a[7]-a[6]+a[8]-a[5];
        for (int i=10; i<=n; i+=2){
            ll s4=a[i]-a[i-3]+a[i-1]-a[i-2];
            ll s6=a[i]-a[i-5]+a[i-1]-a[i-2]+a[i-3]-a[i-4];
            dp[i]=min(dp[i-4]+s4,dp[i-6]+s6);
        }
        printf("%lld\n",ans+dp[n]);
    }
    return 0;
}

 

posted @ 2020-07-20 23:17  lonely_wind  阅读(240)  评论(0编辑  收藏  举报