|
|
|
|
package class45;
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
|
|
|
|
// 来自京东笔试
|
|
|
|
|
// 小明手中有n块积木,并且小明知道每块积木的重量。现在小明希望将这些积木堆起来
|
|
|
|
|
// 要求是任意一块积木如果想堆在另一块积木上面,那么要求:
|
|
|
|
|
// 1) 上面的积木重量不能小于下面的积木重量
|
|
|
|
|
// 2) 上面积木的重量减去下面积木的重量不能超过x
|
|
|
|
|
// 3) 每堆中最下面的积木没有重量要求
|
|
|
|
|
// 现在小明有一个机会,除了这n块积木,还可以获得k块任意重量的积木。
|
|
|
|
|
// 小明希望将积木堆在一起,同时希望积木堆的数量越少越好,你能帮他找到最好的方案么?
|
|
|
|
|
// 输入描述:
|
|
|
|
|
// 第一行三个整数n,k,x,1<=n<=200000,0<=x,k<=1000000000
|
|
|
|
|
// 第二行n个整数,表示积木的重量,任意整数范围都在[1,1000000000]
|
|
|
|
|
// 样例输出:
|
|
|
|
|
// 13 1 38
|
|
|
|
|
// 20 20 80 70 70 70 420 5 1 5 1 60 90
|
|
|
|
|
// 1 1 5 5 20 20 60 70 70 70 80 90 420 -> 只有1块魔法积木,x = 38
|
|
|
|
|
// 输出:2
|
|
|
|
|
// 解释:
|
|
|
|
|
// 两堆分别是
|
|
|
|
|
// 1 1 5 5 20 20 (50) 60 70 70 70 80 90
|
|
|
|
|
// 420
|
|
|
|
|
// 其中x是一个任意重量的积木,夹在20和60之间可以让积木继续往上搭
|
|
|
|
|
public class Code01_SplitBuildingBlock {
|
|
|
|
|
|
|
|
|
|
// 这是启发解
|
|
|
|
|
// arr是从小到大排序的,x是限制,固定参数
|
|
|
|
|
// 当前来到i位置,积木重量arr[i]
|
|
|
|
|
// 潜台词 : 当前i位置的积木在一个堆里,堆的开头在哪?之前已经决定了
|
|
|
|
|
// i i+1 该在一起 or 该用魔法积木弥合 or 该分家
|
|
|
|
|
// 返回值:arr[i....]最少能分几个堆?
|
|
|
|
|
public static int zuo(int[] arr, int x, int i, int r) {
|
|
|
|
|
if (i == arr.length - 1) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
// i没到最后一个数
|
|
|
|
|
if (arr[i + 1] - arr[i] <= x) { // 一定贴在一起
|
|
|
|
|
return zuo(arr, x, i + 1, r);
|
|
|
|
|
} else { // 弥合!分家
|
|
|
|
|
// 分家
|
|
|
|
|
int p1 = 1 + zuo(arr, x, i + 1, r);
|
|
|
|
|
// 弥合
|
|
|
|
|
int p2 = Integer.MAX_VALUE;
|
|
|
|
|
int need = (arr[i + 1] - arr[i] - 1) / x;
|
|
|
|
|
if (r >= need) {
|
|
|
|
|
p2 = zuo(arr, x, i + 1, r - need);
|
|
|
|
|
}
|
|
|
|
|
return Math.min(p1, p2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 这是最优解
|
|
|
|
|
// arr里装着所有积木的重量
|
|
|
|
|
// k是魔法积木的数量,每一块魔法积木都能变成任何重量
|
|
|
|
|
// x差值,后 - 前 <= x
|
|
|
|
|
public static int minSplit(int[] arr, int k, int x) {
|
|
|
|
|
Arrays.sort(arr);
|
|
|
|
|
int n = arr.length;
|
|
|
|
|
int[] needs = new int[n];
|
|
|
|
|
int size = 0;
|
|
|
|
|
int splits = 1;
|
|
|
|
|
for (int i = 1; i < n; i++) {
|
|
|
|
|
if (arr[i] - arr[i - 1] > x) {
|
|
|
|
|
needs[size++] = arr[i] - arr[i - 1];
|
|
|
|
|
splits++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (splits == 1 || x == 0 || k == 0) {
|
|
|
|
|
return splits;
|
|
|
|
|
}
|
|
|
|
|
// 试图去利用魔法积木,弥合堆!
|
|
|
|
|
Arrays.sort(needs, 0, size);
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
|
int need = (needs[i] - 1) / x;
|
|
|
|
|
if (k >= need) {
|
|
|
|
|
splits--;
|
|
|
|
|
k -= need;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return splits;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|