题目链接
个人思路
一个需要简单操作的前缀和数组,因为需要对价值最大的宝石和价值最小的宝石进行操作,所以肯定少不了进行一个排序。然后,可能就有人想要不使用双指针进行处理,如果最小的两个数之和大于最大的数,那么就减去最大的数;反之,减去最小的两个数;依次进行双指针移动。
我一开始就是这么“愚蠢”的想法,好在出题人给样例比较友好,对于样例中的第四个:
6 2
15 22 12 10 13 11
将这个数组排序之和顺序应该是:
10 11 12 13 15 22
按照我之前那个思路,那么就应该先删掉 10、 11;再删掉 22 .此时数组和为 40, 与样例结果不一致!
那么我们再来看样例结果这个 46,怎么去计算?可以发现,样例是接连删去了最大的两个数:22、 15 得到的,那么也就是我之前的猜想是不成立的。
那么正确的解放应该是怎么样的呢?
对于这 K
次操作,无非 进行 i
次删除前 2*i
小的数,再删除最大的 k - i
个数,而剩下的就是我们要求的结果,因此我们使用 前缀和 + 枚举
来处理。
我们先求出排好序的数组前缀和,然后依次枚举上文提到的 i
,即对最小数的执行次数,依次比较,取最大值。
参考代码
Java
import java.io.*;
import java.util.Arrays;
public class Main {
static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
public static void main(String[] args) {
Scanner sc = new Scanner();
int t = sc.nextInt();
int n, k;
while (t-- > 0) {
n = sc.nextInt();
k = sc.nextInt();
long[] arr = new long[n + 1];
long sum = 0;
for (int i = 1; i <= n; ++i) {
arr[i] = sc.nextInt();
}
Arrays.sort(arr);
long[] prefix = new long[n + 1];
prefix[0] = 0;
for (int i = 1; i <= n; ++i) {
prefix[i] = prefix[i - 1] + arr[i];
}
long res = 0;
for (int i = 0; i <= k; i++) {
// 前面执行i次,后面执行k - i次
res = Math.max(res, prefix[n - (k - i)] - prefix[i * 2]);
}
out.println(res);
}
out.flush();
}
}
class Scanner {
static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public Scanner(){}
public int nextInt() {
try {
st.nextToken();
} catch (IOException e) {
throw new RuntimeException(e);
}
return (int) st.nval;
}
}
C/C++
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 3;
int n, k;
ll arr[N], prefix[N];
void solve()
{
cin >> n >> k;
for(int i = 1; i <= n; ++i)
cin >> arr[i];
sort(arr + 1, arr + n + 1);
for(int i = 1; i <= n; ++i)
prefix[i] = prefix[i - 1] + arr[i];
ll res = 0;
for(int i = 0; i <= k; ++i)
res = max(res, prefix[n - (k - i)] - prefix[i * 2]);
cout << res << '\n';
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
}