一、题目描述
二、算法分析说明与代码编写指导
要求凑出至少 k 个数相同,题目只允许两种方式:
1、每次选一个最小值,将其 +1;
2、每次选一个最大值,将其 -1。
每个最值都改变完后,才可以选下一个改变。所以,我们不妨模拟这个过程。
首先如果已经有某个数出现了至少 k 次,那么直接输出 0。
如果没有,则将数组从小到大排序,然后以将每一个数 a1,a2,……,an 都调整为至少出现 k 次为目标。
设要通过这两种方式最终使得 ai 出现 n 次。那么可以有三种方式调整:
①仅将 a1,a2,……,ai-1 每次都增加 1,直到全部变成 ai。
于是最后共有 i 个 ai。分类讨论:
【1】如果 i < k,那么这种调整方法不符合要求,不计入答案。
调整花费的总次数:
【2】如果 i >= k,则扣掉最后 i - k 次调整。也就是说等效于从 a1 开始不断增加当前最小的数到 ai,增加到出现 k 个 ai 时就停止。
②仅将 ai+1,……,an 每次都减去 1,直到全部变成 ai。
调整花费的总次数:
于是最后共有(n - i + 1)个 ai 。分类讨论:
【1】如果 n - i + 1 < k,那么这种调整方法不符合要求,不计入答案。
调整花费的总次数:
【2】如果 n - i + 1 >= k,则扣掉最后 i - k 次调整。也就是说等效于从 an 开始不断减小当前最大的数到 ai,减小到出现 k 个 ai 时就停止。
调整花费的总次数:
③将 a1 调整为 ai,将 an 调整为 ai,将 a2 调整为 ai,将 an-1 调整为 ai,……。
于是最后共有 n 个 ai。因为总有 n >= k,所以只需扣除最后 n - k 次调整。
调整花费的总次数:
三、AC 代码
#include<cstdio>
#include<algorithm>
#pragma warning(disable:4996)
using namespace std;
const unsigned long long nmax = 200002;
unsigned long long n, k, a[nmax], s[nmax], t[nmax], c[nmax], r = UINT64_MAX, A, B;
int main() {
scanf("%llu%llu", &n, &k); for (unsigned long long i = 1; i <= n; ++i)scanf("%llu", a + i);
sort(a + 1, a + n + 1);
for (unsigned long long i = 1; i <= n; ++i)s[i] = s[i - 1] + a[i];
for (unsigned long long i = n; i >= 1; --i)t[i] = t[i + 1] + a[i];
for (unsigned long long i = 1; i <= n; ++i) {
if (a[i] == a[i - 1])c[i] = c[i - 1] + 1;
else c[i] = 1;
if (c[i] >= k) { puts("0"); return 0; }
}
for (unsigned long long i = 1; i <= n; ++i) {
A = i * a[i] - s[i] - (i - k); B = t[i] - (n - i + 1) * a[i] - (n - i + 1 - k);
if (i >= k) { r = min(r, A); }
if (n - i + 1 >= k) { r = min(r, B); }
r = min(r, A + B - k + 1);
}
printf("%llu\n", r);
return 0;
}