题目描述
给你一个数组a1,a2,...,an,其中所有元素都是不同的。
你必须对其进行k次操作。在每个操作中,你需要做以下两个操作中的一个(每次任选其中之一即可):
1.找到数组中的两个最小元素,并删除它们;
2.找到数组中的最大元素,并删除它。
计算出k次操作后,所得数组的最大元素之和。
输入描述
第一行包含一个整数 t (1≤t≤104) — 测试用例的数量。
每个测试用例由两行组成:
第一行包含两个整数 n和 k (3≤n≤2⋅10^5; 1≤k≤99999; 2k<n) — 元素和操作的数量。
第二行包含n个整数 a1,a2,...,an (1≤ai≤10^9;所有 ai不同) — 数组的元素。
数据保证n的总和不超过2⋅105。
输出描述
对于每个测试用例,输出一个整数 — 结果数组中元素的最大可能总和。
输入样例
6
5 1
2 5 1 10 6
5 2
2 5 1 10 6
3 1
1 2 3
6 1
15 22 12 10 13 11
6 2
15 22 12 10 13 11
5 1
999999996 999999999 999999997 999999998 999999995
输出样例
21
11
3
62
46
3999999986
样例解释
在第一个测试用例中,使用第一个操作会产生以下结果:
*两个最小值为 1和 2;删除它们会使数组保留为 [5,10,6],总和为 21;
*最大值为 10;删除它会使数组保留为 [2,5,1,6],总和为 14.
很明显,21是最好的答案。
在第二个测试用例中,最优选择是一次删除两个最小值,一次删除最大值,这样就能得到11。
解题思路
对于这道题,我们首先想到的是对操作次数k依次枚举,我们可以执行操作1从0开始枚举,则操作2从k开始枚举,由此算出删除数总和的最小值,我们用总和减去删除数的和的最小值即最终答案。由数据范围知,k最大为99999,则枚举后时间复杂度为O(k^2),显然会直接暴错,所以我们要对代码进行优化。这里在求和的过程中我们可以利用前缀和来进行求解。如下图所示:(以倒数第二个样例为例)
最终代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e5+10;
long long a[N];
long long ans,sum;//ans代表删除数总和
int main()
{
int T;
cin >> T;
while(T--)
{
int n,k;
cin >> n >> k;//n代表数组元素的个数,k代表操作次数
sum=0;//计算每组数据和之前将总和初始化为0
for(int i=1;i<=n;i++)
{
cin >> a[i];
sum+=a[i];
}
sort(a+1,a+1+n);//将数组元素排序
for(int i=1;i<=n;i++)
{
a[i]+=a[i-1];//计算前缀和数组
}
ans=1e20;//计算删除和的最小值,将ans初始化为最大
int t=0;
for(int i=0;i<=k;i++)
{
if(ans>a[t]+a[n]-a[n-k+i])
{
ans=a[t]+a[n]-a[n-k+i];
//a[t]代表删除最小值的和
//n-(k-i)代表删除最小值后剩下的操作数
//a[n]-a[n-k+i]代表删除最大值的和
}
t+=2;
}
cout << sum-ans<<endl;
}
return 0;
}