2020牛客暑期多校训练营(第三场)E.Two Matchings

E.Two Matchings

题目链接-E.Two Matchings
在这里插入图片描述
在这里插入图片描述
题目大意
定义序列 p p p,满足如下要求:

  1. 是长度为 n n n的排列
  2. 满足 p p i = i 且 p i ≠ i p_{p_i}=i且p_i≠i ppi=ipi=i

定义一个字符串的费用为 ( p = ∑ n = 1 N a b s ( a i − a p i ) ) / 2 (p = \sum\limits_{n=1}^Nabs(a_i-a_{pi}))/2 (p=n=1Nabs(aiapi))/2 a a a为给出的权值数组
现求两个满足上述描述的序列 p , q p,q p,q ,同时还要满足任意 p i ≠ q i p_i≠q_i pi=qi,请你求出这两个序列的费用和的最小值是多少

解题思路
规 律 + D P 规律+DP +DP

  • 排列要想满足 p p i = i 且 p i ≠ i p_{p_i}=i且p_i≠i ppi=ipi=i,只需将 [ 1 , 2 … n ] [1,2…n] [1,2n]两两对调即可,要想满足费用和最小,我们可以先对数列 a a a排序,其中一个排列一定是 [ 2 , 1 , 4 , 3 … n , n − 1 ] [2,1,4,3…n,n-1] [2,1,4,3n,n1]这样,即交换相邻两位
  • 然后我们再找费用次小的排列,因为任意 p i ≠ q i p_i≠q_i pi=qi,我们可以建一个偶环,将该偶环旋转一位 [ 2 , 3 … n , 1 ] [2,3…n,1] [2,3n,1],即将第一个数移到最后一位数后面,这样次优的排列就为 [ 3 , 2 , 5 , 4 … 1 , n ] [3,2,5,4…1,n] [3,2,5,41,n]
  • 根据样例,当 n = 6 n=6 n=6时,最小费用就为最优解+次优解 [ ( a 2 − a 1 ) + ( a 4 − a 3 ) + ( a 6 − a 5 ) ] + [ ( a 3 − a 2 ) + ( a 5 − a 4 ) + ( a 1 − a 6 ) ] [(a_2-a_1)+(a_4-a_3)+(a_6-a_5)]+[(a_3-a_2)+(a_5-a_4)+(a_1-a_6)] [(a2a1)+(a4a3)+(a6a5)]+[(a3a2)+(a5a4)+(a1a6)],即 ( a 6 − a 1 ) × 2 (a_6- a_1)×2 (a6a1)×2;当 n = 4 n=4 n=4时,最小费用为 [ ( a 2 − a 1 ) + ( a 4 − a 3 ) ] + [ ( a 3 − a 2 ) + ( a 1 − a 4 ) ] [(a_2-a_1)+(a_4-a_3)]+[(a_3-a_2)+(a_1-a_4)] [(a2a1)+(a4a3)]+[(a3a2)+(a1a4)],即 ( a 4 − a 1 ) × 2 (a_4- a_1)×2 (a4a1)×2
  • 因为 n = 2 n=2 n=2时,无论如何都不能满足任意 p i ≠ q i p_i≠q_i pi=qi,所以可以定义此时所需的费用为INF
  • 然后我们可以发现 n = 8 n=8 n=8时,把它拆成两个 4 4 4费用会更小, n = 10 n=10 n=10时,把它拆成一个 4 4 4和一个 6 6 6费用会更小,所以在这之后的数就可以由多个长度为4的和6的排列拼接而成,我们 d p dp dp来维护最小值即可
  • 具体操作见代码

附上代码

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) (x &(-x))
#define endl '\n'
using namespace std;
const int INF=0x3f3f3f3f;
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const double PI=acos(-1.0);
const double e=exp(1.0);
const double eps=1e-10;
const int M=1e9+7;
const int N=2e5+10;
typedef long long ll;
typedef pair<int,int> PII;
typedef unsigned long long ull;
int a[N],dp[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>a[i];
		sort(a+1,a+n+1);
		dp[0]=0;
		dp[2]=INF;
		dp[4]=a[4]-a[1];
		dp[6]=a[6]-a[1];
		for(int i=6;i<=n;i+=2)
			dp[i]=min(dp[i-4]+a[i]-a[i-3],dp[i-6]+a[i]-a[i-5]);
		cout<<dp[n]*2<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值