题目大意:给出N个人的位置,每个人的最大移动距离是K。问如何移动,让人集中,才能使集中的地方数达到最小(集中的地方人数要大于等于三)
解题思路:因为所给的点是无序的,所以要先排序。
接着用dp[i]表示,以i开始,后面所有的人都按要求集中在一起,最少要集中几个地方
接着找到以i开始,距离小于等于i + 2 * k的那个最大数所在位置,判断一下这个集合有多少个人
如果这个集合里面的人超过超过三个,那么就可以将其中一个人分配出去了,以防下面集合缺人
如果超过4个,就可以分配两个出去
如果刚好是三个,那就不用分配出去了
最多分配出去两个,如果大于等于三个了,就相当于把一个集合分成两个了,不符合题意
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
const int INF = 0x3f3f3f3f;
int val[N], dp[N];
int n, k, cas = 1;
void init() {
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
scanf("%d", &val[i]);
sort(val, val + n);
}
int find(int num) {
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) >> 1;
if (val[mid] <= num) l = mid + 1;
else r = mid - 1;
}
return l;
}
int dfs(int cur) {
if (cur >= n) return 0;
if (~dp[cur]) return dp[cur];
int Min = INF;
int pos = find(val[cur] + 2 * k);
//刚好三个人
if (pos - cur >= 3) {
Min = min(Min, 1 + dfs(pos));
}
//超过三个人,分配一个出去
if (pos - cur >= 4) {
Min = min(Min, 1 + dfs(pos - 1));
}
//超过4个人,分配两个出去
if (pos - cur >= 5) {
Min = min(Min, 1 + dfs(pos - 2));
}
return dp[cur] = Min;
}
void solve() {
memset(dp, -1, sizeof(dp));
dfs(0);
if (dp[0] == INF)
printf("Case %d: -1\n", cas++);
else
printf("Case %d: %d\n", cas++, dp[0]);
}
int main() {
int test;
scanf("%d", &test);
while (test--) {
init();
solve();
}
return 0;
}