题目: 将一个数组分成两部分,不要求两部分所包含的元素个数相等,要求使得这两个部分的和的差值最小。比如对于数组{1,0,1,7,2,4},可以分成{1,0,1,2,4}和{7},使得这两部分的差值最小。
思路:差最小就是说两部分的和最接近,而且和所有数的和SUM的一半也是最接近的。假设用sum1表示第一部分的和,sum2表示第二部分的和,SUM表示所有数的和,那么sum1+sum2=SUM。假设sum1<sum2 那么SUM/2-sum1 = sum2-SUM/2;
所以我们就有目标了,使得sum1<=SUM/2的条件下尽可能的大。也就是说从n个数中选出某些数,使得这些数的和尽可能的接近或者等于所有数的和的一般。这其实就是简单的背包问题了:
背包容量是SUM/2. 每个物体的体积是数的大小,然后尽可能的装满背包。
dp方程:f[i][V] = max(f[i-1][V-v[i]]+v[i], f[i-1][V] )
f[i][V]表示用前i个物体装容量为V的背包能够装下的最大值,f[i-1][V-v[i]]+v[i]表示第i个物体装进背包的情况,f[i-1][V]表示第i件物品不装进背包的情况。
public static int match(int[] arr){
int sum = Arrays.stream(arr).sum();
int len = arr.length;
int halfOfSum = sum/2;
// 确定矩阵二维定义:第一维代表前i个物体,i可为0;第二维代表从0开始的连续容量值
// 确定矩阵长宽,并初始化。因为矩阵第一维和第二维都是从0开始,所以要加一
int matrix_firstDimensionLen = len+1;
int matrix_secondDimensionLen = halfOfSum+1;
int[][] matrix = new int[matrix_firstDimensionLen][matrix_secondDimensionLen];
//初始化矩阵边界为0
for (int i = 0; i < matrix[0].length; i++) {
matrix[0][i] = 0;
}
for (int i = 0; i < matrix.length; i++) {
matrix[i][0] = 0;
}
//arr的下标,是否与matrix的下标冲突:是的
//matrix[i][j]定义:用前i个物体装容量为j的背包能够装下的最大值
//arr[i]定义:第i+1个物体的大小,所以arr[i-1]才是第i个物体的大小
//遍历从矩阵边界开始(不包括边界),所以i = 1, j = 1
for (int i = 1; i < matrix_firstDimensionLen; i++) {
for (int j = 1; j < matrix_secondDimensionLen; j++) {
//如果第i件物体不装进背包
matrix[i][j] = matrix[i-1][j];
//如果第i件物体装进背包 //备注:j - arr[i-1] >= 0防止下标为负
if(j - arr[i-1] >= 0 && matrix[i - 1][j - arr[i-1]] + arr[i-1] > matrix[i][j]){
matrix[i][j] = matrix[i - 1][j - arr[i-1]] + arr[i-1];
}
}
}
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + "\t");
}
System.out.println("");
}
System.out.println(matrix[len][halfOfSum]);
return sum - matrix[len][halfOfSum]*2;
}
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
int value = match(arr);
System.out.println(value);
}
最后矩阵输出如下:
0 0 0 0 0 0 0 0
0 1 1 1 1 1 1 1
0 1 2 3 3 3 3 3
0 1 2 3 4 5 6 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7
7
1