E Two Matchings(2020牛客暑期多校训练营(第三场))(动态规划+找规律)

E Two Matchings(2020牛客暑期多校训练营(第三场))(动态规划+找规律)

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
judge:牛客题库

题目描述

A permutation of length n is an array p = [ p 1 , p 2 , … , p n ] p=[p_1, p_2, \ldots, p_n] p=[p1,p2,,pn], which contains every integer from 1 to n (inclusive) and each number appears exactly once. For example, p=[3,1,4,6,2,5] is a permutation of length 6.

Let’s call a permutation is a matching if and only if p i ≠ i p_i \ne i pi=i and p p i = i p_{p_i} = i ppi=i for all valid i.

You are given an array a = [ a 1 , a 2 , … , a n ] a=[a_1, a_2, \ldots, a_n] a=[a1,a2,,an] ( 0 ≤ a i ≤ 1 0 9 , n ≥ 4 (0 \le a_i \le 10^9, n \ge 4 (0ai109,n4 and n is even). Define the cost of a permutation is ( ∑ i = 1 n a b s ( a i − a p i ) ) / 2 (\sum\limits_{i=1}^n abs(a_i-a_{p_i})) / 2 (i=1nabs(aiapi))/2.

Define two matchings p, q are combinable if and only if p i ≠ q i p_i \ne q_i pi=qi for all i from 1 to n.

Please find two combinable matchings such that the sum of the cost of these two matchings is as small as possible. Output the sum.

输入描述:

The first line contains one integer t ( 1 ≤ t ≤ 5 × 1 0 4 ) t (1 \le t \le 5 \times 10^4) t(1t5×104) — the number of test cases.

Each test contains two lines. The first line contains one integer n ( n ≥ 4 n \ge 4 n4 and n is even), The second line contains n integers a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an ( 0 ≤ a i ≤ 1 0 9 ) (0 \le a_i \le 10^9) (0ai109).

The sum of n across the test cases doesn’t exceed 2 × 1 0 5 2 \times 10^5 2×105.

输出描述:

For each test, output one line which contains only one integer representing the answer of this test.

示例1

输入

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

输出

16
16

说明

In the first test, one possible combinable matchings with the minimum sum of cost are [2,1,4,3] and [4,3,2,1]. The cost of the first matching is (|0-8| + |8-0| + |0-0| + |0-0|) / 2 = 8, the cost of the second matching is (|0-0| + |8-0| + |0-8| + |0-0|) / 2 = 8. So their sum is 8+8=16.

题解

非常有意思的一道题!

先放出官方题解:
在这里插入图片描述
在这里插入图片描述
首先需要知道,问题可以被转化为把给定的权值进行两两匹配,并求出总权值最小的两种匹配方式的权值和的和。

p i ≠ i p_i \ne i pi=i and p p i = i p_{p_i} = i ppi=i的意思就是把这些数字进行两两匹配。

p i ≠ q i p_i \ne q_i pi=qi是指p个q对应的两种匹配里,不能出现相同的“边”,即p中的任何一对匹配都不能在q里出现。

( ∑ i = 1 n a b s ( a i − a p i ) ) / 2 (\sum\limits_{i=1}^n abs(a_i-a_{p_i})) / 2 (i=1nabs(aiapi))/2就是让求出匹配的权值和。因为同一对数字的差值会被计算两次,所以除2之后就是计算一次的总和了。

那么容易想到需要把a数组进行排序,然后权值和最小的匹配就是从1到n把挨着的两个数字进行匹配。(即a[0]和a[1]匹配,a[2]和a[3]匹配…)

举个例子证明一下:

3 1 4 1 5 9
排序后:
1 1 3 4 5 9

数组:1 1 3 4 5 9
邻差: 0 2 1 1 4

我们把3和4匹配的代价就是3和4的差值,即1,也就相当于在3和4之间画一段线段,代价就是线段覆盖的邻差之和。我们要使权值和最小,也就是覆盖所有的点的同时把每一个线段的长度都是最小,长度最小就是1,即挨着的数字匹配权值最小。如果存在一个长度为4的线段,那么一定存在一个区间内的线段互相交叉,这样就会有一些邻差被重复计算。并且延长了的,彼此交叉的线段还覆盖了一些以前没有计算的邻差。由于邻差都是正数,所以总权值一定变大了。

那么第二小的呢?

这里有个规律。即最小的匹配方式是两两匹配,不同对之间互不交叉。第二小的匹配方式是以4个或6个为一组,组内相互交叉,不同组之间互不交叉。

举个例子:

数组:a b c d e f g h
邻差: o p q r s t u

若以8个为一组进行交叉匹配,那么最优的匹配方式是o+p*2+q+r*2+s+t*2+u,而分割成两段长度为4的区间的话代价是o+p*2+q+s+t*2+u,由于邻差都是正数,所以分成两长度为个4的区间必定大于一个长度为8的区间。

同理,两个长度为6的区间必定优于一个长度为12的区间。

那么一个长度为12的区间分成3个长度为4的区间更优还是分成2个长度为6的区间更优呢?

可能有的人就说当然是3个长度为4的更优啦,因为分的组越多就能越少计算一些邻差了,比如我们队

事实真的是这样吗?

举个例子:

数组:a b c d e f g h i j k l
邻差: o p q r s t u v w x y

若分成3个长度为4的区间,代价是o+p*2+q+s+t*2+u+w+x*2+y

若分成2个场地为6的区间,代价是o+p*2+q+r*2+s+u+v*2+w+x*2+y

做差可得t*2-r*2-v*2。说明4和6并不是某一个一定最优的,这个时候就需要具体情况具体分析了。由此可得结论:任何长度为偶数的数组( n ≥ 4 n\ge4 n4)的次优匹配一定是由长度为4或6的组构成。

接下来我们就可以用dp进行求解了。

设dp[i]为前i个数字可以匹配的最优解(i为偶数)。

当n=4时,次优解为前4个分成一组。

当n=6时,次优解为前6个分成一组。

当n=8时,次优解为前4个分成一组,后4个分成一组。

当n>8时, d p [ i ] = m i n ( d p [ i − 4 ] + g e t V 4 ( i − 3 ) , d p [ i − 6 ] + g e t V 6 ( i − 5 ) ) dp[i] = min(dp[i - 4] + getV4(i - 3), dp[i - 6] + getV6(i - 5)) dp[i]=min(dp[i4]+getV4(i3),dp[i6]+getV6(i5))

代码

#include <bits/stdc++.h>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define sc(x) scanf("%d", &x)
using namespace std;
typedef long long LL;
const int maxn = 2000005;

int n, a[maxn];
LL dp[maxn];

void init() {
    
    
    _for(i, n + 100) a[i] = dp[i] = 0;
}

int getV4(int be) {
    
    
    return a[be + 2] - a[be] + a[be + 3] - a[be + 1];
}
int getV6(int be) {
    
    
    return a[be + 2] - a[be] + a[be + 4] - a[be + 1] + a[be + 5] - a[be + 3];
}

void sol() {
    
    
    init();
    _for(i, n) sc(a[i]);
    sort(a, a + n);
    LL ans = 0;
    for(int i = 0; i < n; i += 2) ans += a[i + 1] - a[i];
    dp[3] = getV4(0);
    dp[5] = getV6(0);
    dp[7] = getV4(0) + getV4(4);
    for(int i = 9; i < n; i += 2) dp[i] = min(dp[i - 4] + getV4(i - 3), dp[i - 6] + getV6(i - 5));
    printf("%lld\n", ans + dp[n - 1]);
}

int main() {
    
    
    int T;
    while(cin>>T) {
    
    
        _for(i, T) {
    
    
            sc(n);
            sol();
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/107443319