第28场 算法赛 第四题题解
表演队
idea: 前缀和+滑动窗口
#include <iostream>
#include <algorithm>
using namespace std;
// 定义数组来存储每个同学的表演能力值
const int MAXN = 100005;
int ability[MAXN];
// 定义前缀和数组,用于快速计算区间内表演能力值的总和
long long prefixSum[MAXN];
int main() {
int totalStudents, selectedStudents;
// 读取同学的总数 totalStudents 和需要挑选的同学数量 selectedStudents
cin >> totalStudents >> selectedStudents;
for (int i = 1; i <= totalStudents; i++) {
cin >> ability[i];
}
// 对表演能力值数组进行排序,使得相近能力值的同学相邻
sort(ability + 1, ability + 1 + totalStudents);
// 初始化当前差异值和最小差异值
long long currentDiff = 0;
long long minDiff = 9e18;
// 开始遍历每个同学,同时构建前缀和数组并计算差异值
for (int i = 1; i <= totalStudents; i++) {
// 计算前缀和,prefixSum[i] 表示前 i 个同学表演能力值的总和
prefixSum[i] = prefixSum[i - 1] + ability[i];
if (i < selectedStudents) {
// 当还未选够 selectedStudents 个同学时
// 计算当前同学与前面已选同学表演能力值的差异并累加到 currentDiff
currentDiff += (long long)ability[i] * (i - 1) - prefixSum[i - 1];
} else if (i == selectedStudents) {
// 当刚好选够 selectedStudents 个同学时
// 同样计算当前同学与前面已选同学表演能力值的差异并累加到 currentDiff
currentDiff += (long long)ability[i] * (i - 1) - prefixSum[i - 1];
// 更新最小差异值
minDiff = min(minDiff, currentDiff);
} else {
// 当已经选了超过 selectedStudents 个同学时,使用滑动窗口的思想
// 计算新加入的同学与当前窗口内其他同学的差异
currentDiff += (long long)ability[i] * (selectedStudents - 1) - (prefixSum[i - 1] - prefixSum[i - selectedStudents]);
// 减去滑出窗口的同学对差异值的影响
currentDiff -= prefixSum[i - 1] - prefixSum[i - selectedStudents] - (long long)ability[i - selectedStudents] * (selectedStudents - 1);
// 更新最小差异值
minDiff = min(minDiff, currentDiff);
}
}
// 输出最小差异值
cout << minDiff << '\n';
return 0;
}