/**
* 一个 正整数数组,长度为2n,将它分成两个数组,长度均为n,使得这两个数组的和最接近。
*
* 思路:
* 记原数组的元素和为sum,从2n个元素中挑出n个,使得其和最接近并且小于等于sum/2。
*
* 动态规划
* 结果集必然以某一个数结尾,所以可以枚举每一个元素。
* 在每一次枚举过程中,以该元素k结尾,然后看是否可以在前k-1找到i-1个数,使得它们之和等于j-a[k]
*
* 注意,是正整数,如果有负数,就失效。
*
* 此时,更通用的做法是,组合算法。从2n个数选出n个,计算和,然后取最接近sum/2的那个。
* 可以先排序,从大到小,然后递归加剪枝。
*
*
*/
public class ArrayDivide2 {
public int divide(int[] a){
int n = a.length/2;
int sum = 0;
for(int i=0; i<2*n; i++){
sum += a[i];
}
//表示是否可以找到i个数,使得它们之和等于j
//参考ArrayDivide1, 本来要使用三维数组b[i][j][k],表示前i个选取j个元素,其和等于k,是否存在,i只依赖与i-1,可用二维数组代替
//b[i][j]表示,取i个数,其和等于j,是否存在
//含义就是,上一次二维数组为true的元素,在本次肯定还是为true,本次只需要增加新的true
//继承上轮循环的值,并且设置新的true
boolean[][] b = new boolean[n+1][sum/2+1];
b[0][0] = true;
//外层循环,前k个数,从1到2n-1
//每一次外围循环,都要从头刷新这个二维数组
for(int k=1; k<2*n; k++){
//单次更新数组
for(int i=1; i<=k&&i<=n; i++){
//更新某一行
for(int j=1; j<=sum/2; j++){
//更新某一个
if(j>=a[k] && b[i-1][j-a[k]]){
b[i][j] = true;
}
}
}
}
//找到第一个,返回
for(int i=sum/2; i>=0; i--) {
if(b[n][i]) {
return i;
}
}
return -1;
}
/**
* @param args
*/
public static void main(String[] args) {
}
}
拆分正整数数组,使二者和最接近,且长度相等。
最新推荐文章于 2022-11-15 14:39:32 发布