题意
给定 n n n 个数的原始权值 w i w_i wi,你要按照某种顺序将它们排序。
若第 i i i 个数的原始权值为 k k k,则第 i + 1 i+1 i+1 到第 n n n 个数的权值均加上 ( − 1 ) i + k + 1 × k (-1)^{i+k+1} \times k (−1)i+k+1×k。
你需要求出一种排序方案,使得 n n n 个数的权值和最大。
solution
理论分析
对于位置 i i i 上的数,它的总贡献为 ( n − i ) ( − 1 ) i + k + 1 × k (n-i)(-1)^{i+k+1} \times k (n−i)(−1)i+k+1×k
一个很明显的贪心策略就是贡献大的数放在前面。
那么对于位置 i i i 我们如何确定应该放哪个数呢?最暴力的方法就是枚举每一个数,计算它的贡献,找出最大的。
即
max { ( − 1 ) i + w i + 1 × w i } \max\{(-1)^{i+w_i+1} \times w_i\} max{(−1)i+wi+1×wi}
很显然,这样的做法是一定会超时的。我们要做的就是缩小候选数的范围,也就是找出哪些数是有可能为最优答案的,一定不可能的我们就不枚举它。
观察计算贡献的式子,不难发现候选数的奇偶和正负均会影响贡献的正负。
也就是说一共有四种组合:
正奇数
正偶数
负奇数
负偶数
对于 i i i 是奇数的情况,我们要从能得到正贡献的两个组合(即正偶数和负奇数)中选绝对值更大的。
如果没有可以得到正贡献的数怎么办,此时只存在负偶数和正奇数,我们要选择绝对值最小的。
可以发现不管是否有正贡献,当 i i i 为奇数时我们选择的都是最大的偶数和最小的奇数。
同理,我们可以推出当 i i i 为偶数时我们选择的都是最大的奇数和最小的偶数。
所以对于每一个位置,我们只需要比较最大的奇数,最大的偶数,最小的奇数,最小的偶数就能得到最大贡献。
实现方法
我们在输入时将奇数和偶数分别放入两个队列中,并从小到大排序,这样每次只需取出两个队列的队头和队尾这四个数进行比较,就能确定答案。
int js[100010];//奇数队列
int os[100010];//偶数队列
int p=1,up;//奇数队头和队尾
int q=1,down;//偶数队头和队尾
for(int i=1,w;i<=n;i++){
cin>>w;
if(w%2) up++,js[up]=w;
else down++,os[down]=w;
}
//排序
sort(js+1,js+up+1);
sort(os+1,os+down+1);
接下来确定每一个位置上的数,要注意的是我们需要判断队列是否为空。
for(int i=1;i<=n;i++){
int addp=((i+js[p]+1)%2==0)?js[p]:-js[p];//最小奇数的贡献
int addq=((i+os[q]+1)%2==0)?os[q]:-os[q];//最小偶数的贡献
int addup=((i+js[up]+1)%2==0)?js[up]:-js[up];//最大奇数的贡献
int adddown=((i+os[down]+1)%2==0)?os[down]:-os[down];//最大偶数的贡献
if(p>up) addp=addup=-inf;//奇数已经取完
if(q>down) addq=adddown=-inf;//偶数已经取完
if(addp==max(max(max(addp,addq),addup),adddown)){
f[i]=js[p];
p++;
}else if(addq==max(max(max(addp,addq),addup),adddown)){
f[i]=os[q];
q++;
}else if(addup==max(max(max(addp,addq),addup),adddown)){
f[i]=js[up];
up--;
}else {
f[i]=os[down];
down--;
}
}
完成这一步后我们就得到了一个重新排好顺序的数组 f f f,按题目要求求出答案即可
ll calc(){
ll add=0;
ll sum=0;
for(int i=1;i<=n;i++){
sum+=f[i]+add;
add+=((i+f[i]+1)%2==0)?f[i]:-f[i];
}
return sum;
}
完整代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,ans;
int up,down,p=1,q=1,inf=1e9+7;
int f[100010];
int js[100010];
int os[100010];
ll calc(){
ll add=0;
ll sum=0;
for(int i=1;i<=n;i++){
sum+=f[i]+add;
add+=((i+f[i]+1)%2==0)?f[i]:-f[i];
}
return sum;
}
int main(){
cin>>n;
for(int i=1,w;i<=n;i++){
cin>>w;
if(w%2) up++,js[up]=w;
else down++,os[down]=w;
}
sort(js+1,js+up+1);
sort(os+1,os+down+1);
for(int i=1;i<=n;i++){
int addp=((i+js[p]+1)%2==0)?js[p]:-js[p];
int addq=((i+os[q]+1)%2==0)?os[q]:-os[q];
int addup=((i+js[up]+1)%2==0)?js[up]:-js[up];
int adddown=((i+os[down]+1)%2==0)?os[down]:-os[down];
if(p>up) addp=addup=-inf;
if(q>down) addq=adddown=-inf;
if(addp==max(max(max(addp,addq),addup),adddown)){
f[i]=js[p];
p++;
}else if(addq==max(max(max(addp,addq),addup),adddown)){
f[i]=os[q];
q++;
}else if(addup==max(max(max(addp,addq),addup),adddown)){
f[i]=js[up];
up--;
}else {
f[i]=os[down];
down--;
}
}
cout<<calc();
return 0;
}