【2020牛客第三场】E-Two Matchings

集训队前辈要大家分享一下题解,这边写一发E题题解(什么)
题目链接:戳这里
前排提示:想直接看“正解”可以往下拉。上面一大段是我们队的思考历程留做参考
大意:让你构造两个排列,两种不同的整个序列的两两匹配,使得所有两两匹配的差的和最小,输出这个和。(原题多读几遍即可理解)
思路分析:显然想让你构造两个序列,现在我们把这两个序列称作最小序列和次小序列最小序列显然非常好构造,我们只需要把a数组排个序,把第1个第2个当作一对,第3个第4个当作一对。。以此类推。 然后每一对都做个差加起来即是最小序列的cost。关键是次小的序列我们该如何构造。赛时我们队花了两个小时在想如何贪心。最后WA了5次 那么这道题的正解是把数列四六分(分为长度为4的块和长度为6的块,每个块内部做两种不同的排列,这样才能使得总cost最小,显然是dp可做)。那么看到这里你一定会想为什么一定是四六分呢?其实这是我们队在贪心的时候,交上去WA了debug时发现的。我们在贪心构造次小序列时,方法是:如样例2,a数组排完序后是1 1 3 4 5 9那么我们就想着从前往后,变成1 3 1 5 4 9,就是1 3换 4 5换,这样和最小序列相比,那肯定多付出的代价是最小的。也测了几个数据都过了。后来发现如在n=8,a数组是1 1 3 4 5 6 8 9时,4和5是不需要换的,因为变成1 3 1 4 5 8 6 9这样子 也不会和最小序列冲突 所以我们就想了,实际上前4和后4成了一组,那是不是以4为一组去划分呢?后来再一想,那n=10的时候呢,前四个一组以后,后面剩下的6个是不是按照之前的方式去贪心呢?再后来又WA了,我们又想,是不是把前六个化成一组,后4个化成一组,能够使结果更小呢?之后又做了每组分裂点的差,还排了序,最后都WA了。。。
样例2我们最开始的思路
于是最后我们赶在比赛结束前,用一个简单的dp,(宿舍李大师5分钟出正解)
正解:由于次优序列和最有序列不能有重复,而且四六分时究竟是那一部分是4那一部分是6都不确定且会影响后面的取法,具有后效性,我们dp来解。
dp前的准备
1. dp[i] := 对于第i个位置,我是把它和它前面3个的数,分成一个四人组、还是和它前面5个数,分成一个六人组。dp的值表示目前的cost。
2. 再来观察规律,如果对于a数组是1,2,3,4,5,6。那么它的次优就是2,3,4,5,6,1(往右移动),具体想法可以通过画图连数看出。
然后我们计算一下,如果是n=6,最优加次优的cost值刚好是:[(2-1) + (4-3) + (6-5)] + [(3-2)+(5-4)+(6-1)] = 2*(6-1);如果是n=4同样:最后算出是2*(4-1),如果是n=8,那就分成俩4人组,cost为2*[(4-1) + (8-5)]n>=10时,就有到底是分4人组还是分6人组的问题了,所以dp来算。
那么就可以开始dp了。对于每一个a[i]的位置,如果这个i>=6了,那么可以把他分为6个一组了,

dp[i] = min(dp[i], dp[i - 6] + a[i] - a[i - 5]);//i>=6时(a[i]~a[i-5]是个6人组)

那其余情况i>=4的情况(我们从i从4开始),都是可以分为4个一组的,那么:

dp[i] = min(dp[i], dp[i - 4] + a[i] - a[i - 3]);//(a[i]~a[i-3]是个4人组)

这是最好想的写法,当然赛后我把它整合为:

dp[i] = min(dp[i - 4] + a[i] - a[i - 3],dp[i - 6] + a[i] - a[i - 5]);

完整代码:

const int N = 2e5 + 10;
ll a[N], res = 1e18;
ll pre[N];
ll dp[N];
int main()
{
    int T, n;
    cin >> T;
    while (T--)
    {
        cin >> n;
        for (int i = 1; i <= n; i++)//输入a数组
        {
            dp[i] = 1e18;
            cin >> a[i];
        }
        sort(a + 1, a + 1 + n);
        dp[0] = 0;
        for (int i = 4; i <= n; i += 2)
        {
            if (i >= 6)
                dp[i] = min(dp[i], dp[i - 6] + a[i] - a[i - 5]);
            dp[i] = min(dp[i], dp[i - 4] + a[i] - a[i - 3]);
        }
        cout << 2*dp[n] << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值