将数组分成两个数组,并最小化数组和的差II
给定一个正数数组arr,请把arr中所有的数分成两个集合 如果arr长度为偶数,两个集合包含数的个数要一样多 如果arr长度为奇数,两个集合包含数的个数必须只差一个 请尽量让两个集合的累加和接近 返回最接近的情况下,较小集合的累加和.
暴力递归
解题思路
先计算数组的累加和sum,我们递归时让选出来的数字累加和逼近于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;
}
//长度是偶数时,只有一个选择
if ((arr.length & 1) == 0){
return process(arr,0,sum / 2,arr.length / 2);
}else{
//长度为奇数时,讨论两种情况
return Math.max(process(arr,0,sum / 2,arr.length / 2),process(arr,0,sum / 2,arr.length / 2 + 1));
}
}
/**
* 递归
* @param arr 数组
* @param index 下标
* @param rest 累加和的目标值
* @param size 需要选择的元素个数
* @return
*/
public static int process(int[] arr,int index,int rest,int size){
//base case 越界时,没有可选择的数了
if (index == arr.length){
//如果到最后一个位置,需要选择的元素,刚好选完,就没有可选了,返回0.
// size 不等于0,之前的选择是无效的,返回-1 标记
return size == 0 ? 0 : -1;
}
//当前元素不选择时,index + 1 ,去下一个位置选,
int p1 = process(arr,index + 1,rest,size);
//无效值时我们用-1 标记,因此p2 也要用-1 初始化,避免比较结果时产生影响
int p2 = -1;
if (arr[index] <= rest){
//选择时,去下一个位置继续选择index + 1
//累加和减去已经选择的rest - arr[index]
//需要选择的元素个数减一
int next = process(arr,index + 1,rest - arr[index],size - 1);
if (next != -1){
p2 = arr[index] + next;
}
}
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;
int M = (N + 1)/2;
//动态规划表
int[][][]dp = new int[N + 1][M + 1][binarySum + 1];
//先把值都初始化为-1
for (int i = 0; i <= N;i++){
for (int j = 0;j <= M;j++){
for (int k = 0;k <= binarySum;k++){
dp[i][j][k] = -1;
}
}
}
//根据base case 初始化已经确定的值
for (int k = 0;k <= binarySum;k++){
dp[N][0][k] = 0;
}
//递归过程改写
for (int i = N - 1;i >= 0;i--){
for (int j = 0;j <= M;j++){
for (int k = 0 ; k <= binarySum;k++){
int p1 = dp[i+1][j][k];
int p2 = -1;
if (j - 1 >= 0 && arr[i] <= k){
//选择时,去下一个位置继续选择index + 1
//累加和减去已经选择的rest - arr[index]
//需要选择的元素个数减一
int next = dp[i+1][j - 1][k - arr[i]];
if (next != -1){
p2 = arr[i] + next;
}
}
dp[i][j][k] = Math.max(p1,p2);
}
}
}
//数组长度不同时的情况讨论
if (N % 2 == 0){
return dp[0][N / 2][binarySum];
}else{
return Math.max(dp[0][N / 2][binarySum],dp[0][N / 2 + 1][binarySum]);
}
}