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;//华丽结束
}

浙公网安备 33010602011771号