关闭

数组分割

标签: 数组分割
259人阅读 评论(0) 收藏 举报
分类:

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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:85138次
    • 积分:1777
    • 等级:
    • 排名:千里之外
    • 原创:91篇
    • 转载:43篇
    • 译文:0篇
    • 评论:7条
    最新评论