题目大意
长度为n的排列是一个数组p = [p1,p2,...,pn] ,其中每个整数为1到n中的一个数,且每个数恰好出现一次。本题对于任意i,。(排列中的数两两对应)
给定一个数组a (0≤a≤10^9,并且n是偶数且大于等于4)。 输出排列的最小和次小成本。(每对数之差的总和)
解题思路
显然最小花费很好求,对元素进行排序,对相邻的两两求差,求和即可。
次小的就比较棘手,我们先举几组样例,从小到大排序,并找出每组的次小匹配方案。将每个被减数标上'+'号,每个减数标'-'号。
1 1 3 4 5 9 | 2 3 5 8 | 1 3 5 6 | 7 8 9 10 13 14 |
---|---|---|---|
- - + - + + | - - + + | - - + + | - - + - + + |
可以发现,四个一组的永远是"- - + +",而六个一组的永远是"- - + - + +"。
每4个元素一组的次小即为a[4]-a[2]+a[3]-a[1]=a[4]+a[3]-a[2]-a[1],且每6个元素的次小花费即为a[6]-a[1]+a[3]-a[2]+a[5]-a[4]。
!!!由此想到,任何数列都可以拆成4个元素和6个元素一组的组合,而次小方案中以上的方案是固定的!!!
所以就可以使用dp了,dp[i] 表示前i个数的次大值为dp[i]。所以可以得到:
dp[i]=min(dp[i-4]+a[i]+a[i-1]-a[i-2]-a[i-3],dp[i-6]+a[i]+a[i-1]-a[i-2]+a[i-3]-a[i-4]-a[i-5]);
下面直接上代码:
AC代码
#include #define ll long long using namespace std; ll a[200010],dp[200010]; int main() { ll t,n,ans,i; scanf("%lld",&t); while(t--) { ans=0; scanf("%lld",&n); for(i=1;i<=n;i++) scanf("%lld",&a[i]); sort(a+1,a+n+1); for(i=2;i<=n;i+=2) ans+=a[i]-a[i-1]; dp[2]=1ll<<60; dp[4]=a[4]+a[3]-a[2]-a[1]; dp[6]=a[6]+a[5]-a[4]+a[3]-a[2]-a[1]; for(i=8;i<=n;i+=2) dp[i]=min(dp[i-4]+a[i]+a[i-1]-a[i-2]-a[i-3],dp[i-6]+a[i]+a[i-1]-a[i-2]+a[i-3]-a[i-4]-a[i-5]); printf("%lld\n",ans+dp[n]); } }