数组分割——解题笔记

数组分割——解题笔记

    

    题目:有一个没有排序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近。


    分析:这道题目可以用动态规划求解,或者说是一个典型的0,1背包问题,对于第i的数,到底是放进去还是不放,就要看放了对结果有什么影响,不放对结果又有什么影响。而结果是依据题目而言的,这道题目中的结果就是数组之和。

    注意,一般动态规划数组中,需要先初始化所有i的结果,初始化可以为1,或者0. 然后,从前往后,依次得到每个i的优化结果,而且需要注意的是,第i个的结果只和第i-1个的结果有关

    在这道题目中,我参考了参考中的一篇博文,感觉讲的比较清楚,如下:



给出的伪代码为:

F[][][]← 0

for i ← 1 to 2*N

    nLimit ← min(i,N)

    do for j ← 1 to nLimit

        do for k ← 1 to Sum/2

            F[i][j][k] ← F[i-1][j][k]

            if (k >= A[i] && F[i][j][k] < F[i-1][j-1][k-A[i]]+A[i])

                then F[i][j][k] ← F[i-1][j-1][k-A[i]]+A[i]

return F[2N][N][Sum/2]


    核心函数如下:


int splitArray(int a[], int len, int sum)
{
	int*** X; 
	X = new int**[len+1]; 
	for(int p = 0; p < len/2+1; p++)
	{
		X[p] = new int*[len/2+1]; 
		for(int q= 0; q < sum+1; q++)
			X[p][q] = new int[sum+1]; 
	}

	for(int i = 1; i <= len; i++)
	{
		int lim = min(i, len/2); 
		for(int j = 1; j <= lim; j++)
		{
			for(int k = 1; k <= sum; k++)
			{
				X[i][j][k] = X[i-1][j][k]; 
				if(k >= a[i-1])
				{
					if(X[i][j][k] < X[i-1][j-1][k-a[i-1]] + a[i-1])
					{
						X[i][j][k] = X[i-1][j-1][k-a[i-1]] + a[i-1]; 
					}
				}
			}
		}
	}

	int result = X[len][len/2][sum]; 
	//delete [][][]X; // 销毁空间这种做法是错误的,应该和申请空间方法一样
	return result; 
}


注意:其中申请三维动态空间的方法,以及销毁的方法。


    上面解法的空间复杂度为O(N^2Sum),而且用到的是三维空间,是可以进行优化的。

我们观察前面不含路径的伪代码可以看出,F[i][j][k]只与F[i-1][][]有关,这一点状态方程上也能反映出来。所以我们可以用二维数组来代替三维数组来达到降低空间复杂度的目的。但是怎么代替里面存有玄机,我们因为F[i][j][k]只与F[i-1][][]有关,所以我们用二维数组来代替的时候应该对F[i][j][k]的“j”维进行逆序遍历。为什么?因为只有这样才能保证计算F[i][j][k]时利用的F[i-1][j][]和F[i-1][j-1][]是真正i-1这个状态的值,如果正序遍历,那么当计算F[][j][]时,F[][j-1][]已经变化,那么计算的结果就是错误的。
伪代码如下
[cpp] view plaincopy
F[][]← 0

for i ← 1 to 2*N

nLimit ← min(i,N)

do for j ← nLimit to 1

do for k ← A[i] to Sum/2

if (F[j][k] < F[j-1][k-A[i]]+A[i])

then F[j][k] ← F[j-1][k-A[i]]+A[i]



return F[N][Sum/2] and Path[][][]



    动态规划的题目写起来还是不顺额。。。


参考:

http://blog.csdn.net/wumuzi520/article/details/7028705

http://wenku.baidu.com/link?url=CQkQIsOqAYP41MuP9yavgklpHiST6BXXDi2zwfvGnRZEDHMF6S7LrwinwMPW3C6YAKkBw2i397aLNtppediKQ2nPJ4BSqFo8KUb0VbmA1Eu


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值