题目:
一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度为20的金条,不管切成长度多大的两半,都要花费20个铜板。
问:一群人想整分整块金条,怎么分最省铜板?
例如,给定数组{10,20,30},代表一共三个人,整块金条长度为10+20+30=60。
金条要分成10,20,30。如果先把长度60的金条分成10和50,花费60;再把长度50的金条分成20和30,花费50;一共花费110铜板。
但是如果先把长度60的金条分成30和30,花费60;再把长度30金条分成10和20,花费30;一共花费90铜板。
输入一个数组,返回分割的最小代价。
1、暴力解决方案
1.1、图解:
1.2、代码:
//方案1:暴力解法
public static int lessMoney1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return process(arr, 0);
}
public static int process(int[] arr, int pre) {
//当数组长度等于的时候,返回最终的结果:pre
if (arr.length == 1) {
return pre;
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
//i和j位置的数相加,然后再放入数组中(生成新的数组)
int[] newArr = copyAndMergeTwo(arr, i, j);
//用新的数组和pre值再次去递归
int newPre = pre + arr[i] + arr[j];
int result = process(newArr, newPre);
ans = Math.min(ans, result);
}
}
return ans;
}
//i和j位置的数相加,然后再放入数组中
//比如:i=0,j=1
//[10,20,30,40] =》 [30,40,30]
public static int[] copyAndMergeTwo(int[] arr, int i, int j) {
int[] ans = new int[arr.length - 1];
int ansi = 0;
for (int arri = 0; arri < arr.length; arri++) {
//把除了i和j位置的数依次拷贝到新数组中
if (arri != i && arri != j) {
ans[ansi++] = arr[arri];
}
}
//最后把i位置和j位置数相加,然后把相加结果放入到新数组中
ans[ansi] = arr[i] + arr[j];
return ans;
}
2、贪心解决方案
2.1、思路:
1、准备一个小根堆
2、把数组元素依次加入到小根堆中
3、然后依次弹出两个数,求和
4、把和扔到小根堆中
循环3、4步骤
2.2、代码:
//方案2:贪心解法
public static int lessMoney2(int[] arr) {
//1、准备一个小根堆
PriorityQueue<Integer> pQ = new PriorityQueue<>();
//2、把所有数字扔到小根堆中
for (int i = 0; i < arr.length; i++) {
pQ.add(arr[i]);
}
int sum = 0;
int cur = 0;
while (pQ.size() > 1) {
//3、每次弹出两个数字进行结合
cur = pQ.poll() + pQ.poll();
sum += cur;
//4、把结合的数扔到小根堆中
pQ.add(cur);
}
return sum;
}