拆分正整数数组,使二者和最接近,且长度相等。

/**
 * 一个 正整数数组,长度为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) {


	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值