$("head").append('')

B4220 [常州市赛 2023] 洗牌

B4220 [常州市赛 2023] 洗牌

题目传送门

题目背景

搬运自 http://czoj.com.cn/p/677。数据为民间数据。

题目描述

小 X 有 \(n\) 张标有数字的纸牌,第 \(i\) 张纸牌上面的数是 \(a_i\),现在小 X 想通过洗牌打乱它们的顺序。

对于一个洗牌后的顺序,小 X 觉得相邻两张纸牌上数的差的绝对值之和越大,牌就洗的 越乱。

举个例子:现在有 \(4\) 张纸牌,纸牌上的数依次为 \([1,2,3,4]\)

假设洗完牌后,纸牌上的数依次 \([4,3,2,1]\),相邻两张纸牌上数的差的绝对值之和为 \(\left|4-3\right|+\left|3-2\right|+\left|2-1\right|=1+1+1=3\)

假设洗完牌后,纸牌上的数依次 \([2,4,1,3]\),相邻两张纸牌上数的差的绝对值之和为 \(\left|4-2\right|+\left|4-1\right|+\left|3-1\right|=2+3+2=7\)

那么小 X 就会觉得 \([2,4,1,3]\) 的顺序比 \([4,3,2,1]\) 更乱。

小 X 想要问问你,对于所有顺序,相邻两张纸牌上数的差的绝对值之和最大能是多少。

输入格式

第一行 \(1\) 个正整数 \(n\),表示纸牌的张数。 第二行 \(n\) 个正整数 \(a_i\),表示第 \(i\) 张纸牌上的数。注意具有相同数字的纸牌可能有多张。

输出格式

输出一行一个整数,表示答案。

输入输出样例 #1

输入 #1

4 
1 2 3 4

输出 #1

7

输入输出样例 #2

输入 #2

5
1 2 3 4 5

输出 #2

11

输入输出样例 #3

输入 #3

10
1 2 3 4 5 6 7 8 9 10

输出 #3

49

说明/提示

本题共有 \(11\) 个测试点。

保证当测试点编号是偶数时,\(n\) 也是偶数。

测试点编号 \(n\) \(a_i\)
\(1\sim3\) \(1\le n\le 10\) \(1\le a_i\le10^6\)
\(4\sim8\) \(1\le n\le 100\) \(1\le a_i\le 10\)
\(9\sim11\) \(1\le n\le10^5\) \(1\le a_i\le 10^6\)


题解

题意:

给定一个序列 \(S\) 要求构造 \(S\) 的排列使得其中相邻两项间差的绝对值之和最大。

思路:

首先,我们可以观察序列,对于不是第 \(1\) 个和第 \(n\) 个的数,都会有两次计算。如果这个数是较大的数,它可能被加两次;反之则可能被减两次。

如果感性理解有困难,可以看以下数据:

对于序列 \([1,2,3,4]\) 来说,如果我们把它排成 \([1 4 2 3]\),那么它就等价于 \((4-1)+(4-2)+(3-2)\)。可见,中间的 \(4,2\) 都被计算了两次。
那么我们要让总的贡献最大,自然要让大的多被加,小的多被减。这样贪心的思路就显而易见了:
尽可能让最大的数和最小的数在中间交错排列,把靠近中位数的两个数放在两边。
然后答案就等于最靠近中位数的两个数比它大的加上自己本身,比他小的减去自己本身,剩下的数按照同样的规则加减两遍就行了。

有了这个思路,偶数个数字就很好处理了:

int num=(n>>1);
for(int i=1;i<=num;i++){
	if(i==num)ans+=a[n-i+1]-a[i];//两个中间数值加一遍
	else ans+=(a[n-i+1]-a[i])*2;//大的数加两遍,小的数减两遍
}
cout<<ans;

不过,当 \(n\) 为奇数的时候,中位数只有一个,该怎么办呢?

反正中位数不是在与大的数相邻的端点就是在与小的数相邻的端点,只需要都算出来比较一下就好了!

仔细观察,对于去掉中位数之后的序列,其端点又生成了两个新的中位数,如果这个中位数加入了大数的行列,则会有 \(\frac{(n+1)}{2}\) 个大数包围着 \(\frac{(n-1)}{2}\) 个小数。
那么这个时候就代表着左右两个端点都是大数,除了这两个数以外的大数被加两次,小数被减两次。
反之,就是小数包围大数,那么依旧除了两个端点的数其他数大的加两次小的减两次。

int num=(n>>1)-1,i=0;
for(i;i<=num;i++){
	ans+=(a[n-i+1]-a[i])*2;
}
i--; 
int num1=a[i+2]+a[i+3]-2*a[i+1],num2=2*a[i+3]-a[i+1]-a[i+2];
cout<<ans+max(num1,num2);

代码

似乎主要的代码都已经给了,而且本题没有什么很坑人的细节,不过还是放一下吧。

#include<bits/stdc++.h>
using namespace std;
long long n,a[100005],ans;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1);
	if(n%2==0){
		int num=(n>>1);
		for(int i=1;i<=num;i++){
			if(i==num)ans+=a[n-i+1]-a[i];
			else ans+=(a[n-i+1]-a[i])*2;
		}
		cout<<ans;
		return 0;
	}
	else{
		int num=(n>>1)-1,i=0;
		for(i;i<=num;i++){
			ans+=(a[n-i+1]-a[i])*2;
		}
		i--; 
		int num1=a[i+2]+a[i+3]-2*a[i+1],num2=2*a[i+3]-a[i+1]-a[i+2];
		cout<<ans+max(num1,num2);
	} 
	return 0;//华丽结束
}

posted @ 2025-12-24 16:50  lain_yc  阅读(13)  评论(0)    收藏  举报
$("head").append('')