题目大意:给你一个数列,从$a_1->a_n$,你需要找到两个匹配序列$p,q$其中$p_i eq 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 ",ans+dp[n]); } return 0; }