数组分割

1. 简述

    有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近?例如有如下数组,1,5,7,8,9,6,3,11,20,17。应该分割为1,3,11,8,20和5,7,9,6,17。

2. 思路

    方法一,暴力搜索,遍历每种分组方法,一共C(2N, N)种组合,复杂度是(2N!/N!),复杂度过高。

    方法二,动态规划,原题是要求求两个数组的和最近接,这等价于要求其中较小的和最接近与2n个正整数的和(设为SUM)的一半。因此,弱化题目,求这个最近接一半的且小于等于SUM/2数值,定义Heap[i],i<=N表示任意i个数能够构成的数值集合。初始化:Heap[0]= 0。更新代码:

for(int i=1; i<2*N; i++) { // 依次读取A[i]更新堆 
for(int j=min{i, N}; j>0; j--) // 更新引入A[i]后可能的元素个数的情况
for each v in Heap[j-1] // 对于引入A[i]的情况
insert(Heap[j], A[i]+v);
}

    关于insert次数,至多为2^(N-1),为什么是这个数我没想明白,总共感觉上应该是C(2N,N)次插入啊。

    方法三,复杂度主要是由于堆很大,原因是我们记录的是各种可能组合出的数值。如果SUM值不高,可以定义bool FLAG[i][j],i=0,1,...,2*N,j=0,1,...,SUM/2,表示是否存在i个数,其和为j。初始化,FLAG[0][0] = true。

复制代码
for(int k=1; k<2*N; k++) {
for(int i=min{k, N}; i>0; i--) { // 每一个可能的长度
for(int v=1; v<SUM/2; v++) { // 每一个可能的可能的数值
if(v>=A[k] && FLAG[i-1][v-A[k]])
FLAG[i-1][v-A[k]] = true;
}
}
}
复制代码

    Max{j},其中,j=0,1,...,2*N,FLAG[N][j]=true,这即为所求。这样复杂度为O(N*N*SUM)级别的,尤其是当SUM相对不大的时候,复杂度会大大降低。

public class Array_cut 
{
	public static void main(String[] args)
	{
		int[] a={1,2,3,4,5,6,7,8};
		compute(a);
	}
	public static void compute(int[] a)
	{
		int sum=0;
		for(int i=0;i<a.length;i++)
			sum = sum+a[i];
		int n=a.length;
		boolean isOk[][] = new boolean[a.length/2+1][sum];
		isOk[0][0]=true;


		for(int i=1;i<isOk.length;i++)
		{
			for(int j=1;j<isOk[0].length;j++)
			{
				isOk[i][j]=false;
			}
		}
		for(int k=1;k<=a.length;k++)
		{
			for(int i=Math.min(k, a.length/2);i>=1;i--)
			{
				for(int v=1;v<=sum/2;v++)
				{
					//System.out.print("k="+k+"i="+i+"v="+v);
					if(v>=a[k-1]&&isOk[i-1][v-a[k-1]])
					{
						isOk[i][v]=true;
					}
				}
			}
		}
		print(isOk,a.length/2);
	}
	public static void print(boolean[][] a,int n)
	{
		for(int i=n;i<a.length;i++)
		{
			for(int j=1;j<a[0].length;j++)
			{
				System.out.print(a[i][j]+" ");
			}
			
		}
	
	}
}
结果如下:

false false false false false false false false false true true true true true true true true true false false false false false false false false false false false false false false false false false 

可以看出来,从中选4个数得到16,17,18都可以。

原文链接:http://www.cnblogs.com/pangxiaodong/archive/2011/10/10/2205366.html

3. 参考

    编程之美,2.18节,数组分割

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值