题目来源
http://codeforces.com/contest/985/problem/C
题意分析
You have m = n·k wooden staves. The i-th stave has length ai. You have to assemble n barrels consisting of k staves each, you can use any k staves to construct a barrel. Each stave must belong to exactly one barrel.
Let volume vj v j of barrel j be equal to the length of the minimal stave in it.
You want to assemble exactly n barrels with the maximal total sum of volumes. But you have to make them equal enough, so a difference between volumes of any pair of the resulting barrels must not exceed l, i.e. |vx − vy| ≤ l for any 1 ≤ x ≤ n and 1 ≤ y ≤ n | v x − v y | ≤ l f o r a n y 1 ≤ x ≤ n a n d 1 ≤ y ≤ n .
Print maximal total sum of volumes of equal enough barrels or 0 if it’s impossible to satisfy the condition above.
Input
The first line contains three space-separated integers n,k and l(1 ≤ n, k ≤ 105,1 ≤ n⋅k ≤ 105,0 ≤ l ≤ 109 n , k a n d l ( 1 ≤ n , k ≤ 10 5 , 1 ≤ n · k ≤ 10 5 , 0 ≤ l ≤ 10 9 ).
The second line contains m = n·k space-separated integers a1, a2, …, am (1 ≤ ai ≤ 109) — lengths of staves.
Output
Print single integer — maximal total sum of the volumes of barrels or 0 if it’s impossible to construct exactly n barrels satisfying the condition |vx − vy| ≤ l for any 1 ≤ x ≤ n and 1 ≤ y ≤ n | v x − v y | ≤ l f o r a n y 1 ≤ x ≤ n a n d 1 ≤ y ≤ n .
Sample Input
input
4 2 1
2 2 1 2 3 2 2 3
output
7
input
2 1 0
10 10
output
20
input
1 2 1
5 2
output
2
input
3 2 1
1 2 3 4 5 6
output
0
Note
In the first example you can form the following barrels: [1, 2], [2, 2], [2, 3], [2, 3].
In the second example you can form the following barrels: [10], [10].
In the third example you can form the following barrels: [2, 5].
In the fourth example difference between volumes of barrels in any partition is at least 2 so it is impossible to make barrels equal enough.
给出 n*k 块木板, 要求组成 n 个木板数为 k 的木桶. 木桶的容积等于木桶的最短木板. 要求任意两个木桶间的容积差小于给定的阈值 l l , 使得k个木桶的容积和最大.
解题思路
这题乍看上去像是桶排序, 然而其实并不是这样.
首先我们考虑题目的约束条件, 即任意木桶容积( 最短木板 )差小于阈值. 那么所有木桶的最短木板只可能出现在最短的这批木板中: .
因此记落在
[min,min+l]
[
m
i
n
,
m
i
n
+
l
]
中的木板数为:
m
m
. 我们接下来分两种情况讨论:
1. , 这种情况下不可能满足任意木桶间容积差小于
l
l
, 输出0返回;
2. , 这时我们需要从 m 块木板中选出 k 块分别作为每个木桶的最短板. 为使木桶的容积和最大, 我们要尽可能的把比较短的木板凑在一起形成同一个木桶, 这样稍长些的木板就可以被选为其它木桶的最短板. 所以这里使用贪心策略:
优先用当前(未使用过的)最短的木板和比它长一点的木板构成木桶, 其中最短的木板作为该木桶的最短板, 直到
- 已经构成了一整个木桶;
- 或剩下的木板数恰好等于未确定最短板的木桶数;
根据以上的分析很容易能写出实现. 但在这里我们还可以进行优化. 因为我们只需要找出 [min,min+l] [ m i n , m i n + l ] 里的木板, 所以我们的任务并不用排序整个n*k, 可以利用变形的堆排序.
建立一个小根堆, 依次弹出堆顶保存. 直到发现当前的堆顶已大于 min+l m i n + l 为止.
代码实现
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<long> entry;
int total;
void insert_heap(long current, int low, int high)
{
int small = 2 * low + 1;
while (small <= high) {
if (small < high&&entry[small] > entry[small + 1]) small++;
if (current <= entry[small]) break;
else {
entry[low] = entry[small];
low = small;
small = 2 * low + 1;
}
}
entry[low] = current;
}
void build_heap()
{
for (int low = total / 2 - 1; low >= 0; low--) {
long current = entry[low];
insert_heap(current, low, total - 1);
}
}
int main()
{
long n, k, threshold, temp;
long long tempAns = 0;
scanf("%ld%ld%ld", &n, &k, &threshold);
total = n*k;
for (int i = 0; i <total; i++) {
scanf("%ld", &temp);
entry.push_back(temp);
}
build_heap();
long last_unsorted, current, sst;
sst = entry[0];
long lst = sst + threshold;
for (last_unsorted = total - 1; last_unsorted > 0; last_unsorted--) {
current = entry[last_unsorted];
if (entry[0] > lst) break;
entry[last_unsorted] = entry[0];
insert_heap(current, 0, last_unsorted - 1);
}
if (entry[0] <= lst) last_unsorted = -1;
if (total - last_unsorted - 1 < n) {
printf("0\n");
return 0;
}
int j, buckets = 0;
long long ret = 0;
int p = total - 1;
for (int i = 1; i <= n; i++) {
ret += entry[p--];
for (int j = 1; j <= k - 1; j++) {
if ((p - last_unsorted + i) <= n) break;
else p--;
}
}
printf("%I64d\n", ret);
return 0;
}