将数组分成两个数组,并最小化数组和的差
给定一个正数数组arr, 请把arr中所有的数分成两个集合,
尽量让两个集合的累加和接近 返回最接近的情况下,较小集合的累加和
暴力递归
解题思路
我们先求出数组的累加和sum.然后拆分数组时,让较小集合去逼近
sum/2.这样得出的数组就是符合要求的.
递归思路:
每到一个数,有选择和不选两种情况,分开讨论,返回最逼近sum/2 的最大值.
代码演示
/**
* 暴力递归求拆分数组
* @param arr
* @return
*/
public static int right(int[] arr) {
if (arr == null || arr.length < 2){
return 0;
}
int sum = 0;
for (int a : arr){
sum += a;
}
return process(arr,0,sum / 2);
}
/**
* 递归分割数组
* 思路:arr[i...]
* 可以自由选择,请返回累加和尽量接近sum,但不能超过sum的情况下,最接近的累加和是多少
* @param arr 数组
* @param index 下标
* @param sum 需要达成的目标
* @return
*/
public static int process(int[]arr,int index,int sum){
if (index == arr.length){
return 0;
}
//不选当前数字的情况
int p1 = process(arr,index + 1,sum);
//选择当前数字的情况
int p2 = 0;
//当前数字 不能比剩余要选择的数字还大.
if (arr[index] <= sum){
//选择index 位置后,剩下需要去完成的任务还剩sum-arr[index]
p2 = arr[index] + process(arr,index+1,sum-arr[index]);
}
//选择接近sum / 2 的最大值,这样两个数组和最接近
return Math.max(p1,p2);
}
动态规划
动态规划就是对递归的改写,还是三个步骤.
1.根据base case 初始化dp表
2.递归过程改成从表中拿数据的过程
3.返回递归调用的最初始状态
代码演示
/**
* 动态规划
* @param arr
* @return
*/
public static int dp(int[]arr){
if (arr == null || arr.length < 2){
return 0;
}
int sum = 0;
for (int a : arr){
sum += a;
}
int binarySum = sum / 2;
int N = arr.length;
//动态规划表
//base case 里 index == N 值为0.java 表初始化本身就是0.不需要在初始化了
int[][]dp = new int[N + 1][binarySum + 1];
//递归过程改成表中拿数据
for (int i = N - 1;i >= 0 ;i--){
for (int j = 0;j <= binarySum;j++){
int p1 = dp[i + 1][j];
//选择当前数字的情况
int p2 = 0;
//当前数字 不能比剩余要选择的数字还大.
if (arr[i] <= j){
p2 = arr[i] + dp[i + 1][j - arr[i]];
}
dp[i][j] = Math.max(p1,p2);
}
}
//返回递归的最初始状态
return dp[0][binarySum];
}