题目描述
给一个整数数组 array,把他分为两组 a 和 b,要使得 a 和 b 两组的元素之和的差最小,输出最小值(或者输出最小值的分组情况)
力扣上面第1049题也是该问题的一种变形问法1049. 最后一块石头的重量 II
问题分析
将一个数组分成两个,使两个数组的和之差最小,要想差最小,必然a组的和 b组的和 都趋近sum/2,换句话说,a组和在sum/2的左边逼近,b组和在sum/2的右边逼近。当然,a组的和逼近sum/2时,b组和也必然逼近。 我们把数字想象成石头,数字大小代表石头重量,a,b两组代表两个背包,所以,这个问题可以转化为在背包a的最大承重量为sum/2前提下,从原数组中拿取石头,使背包a所装的重量最大,即简化版的背包问题。
建立状态转移方程
设f(i,target) 表示 轮到第i个石头选择时,背包最大空间为target时能获得的最大物品重量,
设数组weight[n]表示每个石头的重量,
则状态转移方程如下 f(i,target) = Max(f(i-1,target), f(i,target-weight[i])+weight[i])
f(i-1,target) 表示第i个物品不选时,能获得的最大重量
f(i,target-weight[i])+weight[i] 表示第i个物品选择时,能获得的最大重量
代码实现
/**
* @param stones 待分组的石头
* @return a,b两组的最小差值
*/
public int getMinSub(int[] stones) {
if (stones == null || stones.length == 0) {
return 0;
}
//石头总数
int size = stones.length;
//所有石头的总重量
int sumWeight = 0;
for (int temp : stones) {
sumWeight = sumWeight + temp;
}
//所需背包的最大承受重量为所有石头总重量的一半
int maxWeight = sumWeight / 2 + 1;
//定义一个二维数组表示 轮到第i个石头选择时,背包最大承重为maxWeight时能装的石头最大总重量
int[][] data = new int[size + 1][maxWeight];
//从第一块石头开始遍历
for (int i = 1; i <= size; i++) {
//w代表背包的最大承受重量
for (int w = 0; w < maxWeight; w++) {
//第i个石头不选
int temp1 = data[i - 1][w];
int temp2 = 0;
//第i个石头选了,选之前要先看背包放不放得下
if (stones[i - 1] <= w) {
temp2 = data[i - 1][w - stones[i - 1]] + stones[i - 1];
}
//每块石头都可以选和不选,取最大值
data[i][w] = Math.max(temp1, temp2);
}
}
//a,b两组求差
return Math.abs(sumWeight - data[size][maxWeight - 1] - data[size][maxWeight - 1]);
}