数组分割——解题笔记

数组分割——解题笔记

题目:有一个没有排序、元素个数为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

时间: 2024-10-11 09:57:16

数组分割——解题笔记的相关文章

【编程之美】数组分割

有一个无序.元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近? 分析与解法 从题目中可以分析出,题目的本质就是要从2n个整数中找出n个,使得它们的和尽可能地靠近所有整数之和的一半. 解法一:不靠谱的解法 先将数组的所有元素排序,然后划分为S1 = {a1, a3, ..., a2n-1}和S2 = {a2, a4, ..., a2n}: 从S1和S2中找出一对数进行交换,使得两个集合中所有元素的和之间的差值尽可能的小,直到找不到可对换的.

数组分割问题(转)

题记:这道题和<编程之美>一书中2.18节的数组分割区别不大,但本人觉得<编程之美>这一节讲的不够透彻,不好理解(或许本人愚钝),故给出自己的思路,同时也给出打印其中一种方案的方法(这一点<编程之美>并没有提到). 两个序列大小均为n,序列元素的值为任一整数,无序: 要求通过交换两个序列的元素,使序列a元素之和与序列b的元素之和的差最小(可能存在很多种组合,要求找出其中一种即可). 如序列:1  5   7   8   9和序列6  3   11  20  17我们可以

数组分割问题

昨天同学问我一道关于数组分割的问题——有一个无序.元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并是两个子数组的和最接近. 假设2n个整数之和为sum.从2n个整数中找出n个元素的和,有三种可能:大于sum/2,等于sum/2,小于sum/2.可以考虑小于等于sum/2的情况.使用动态规划解决这个问题,其实这是一个NP问题,只能尽量去接近sum/2这个值. 我们可以定义dp[k][s]代表从前k个数中去任意个元素,且k小于等于n,其和为s是否存在:之所以将选出的

数组分割问题(转载)

问题: 1. 有一个无序.元素个数为2n的正整数数组,要求:如何能把这个数组分割为两个子数组,子数组的元素个数不限,并使两个子数组之和最接近. 2. 有一个无序.元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组之和最接近. 分析: 假设数组A[1..2N]所有元素的和是SUM.模仿动态规划解0-1背包问题的策略,令S(k, i)表示前k个元素中任意i个元素的和的集合.显然:                S(k, 1) = {A[i] | 1<= i

字符串移位包含的问题——解题笔记

字符串移位包含的问题--解题笔记 题目描述:给定两个字符串s1和s2,要求判定s2时候能够被s1做循环移位(rotate)得到的字符串包含.例如,给定s1=AABCD和s2=CDAA,返回true:给定s1=ABCD和s2=ACBD,返回false. solution 1: 直接的方法,把串s1遍历所有移位之后的情况,每种情况都判断一遍时候包含s2. 分析:这种情况在s1串很长的情况不好,需要遍历的情况太多. solution 2:注意到s1串移位之后会出现循环的情况,而循环也就是s1串的重复放

求一个数的最大公约数的三种思路——解题笔记

求一个数的最大公约数的三种思路--解题笔记 编程之美上的题目:求一个数的最大公约数. 这道题目有三种解题思路,总结如下: 思路一: 直接使用辗转相除法,这个不多介绍,代码如下: // 直接辗转相除法 int gcd1(int a, int b) { for(int m = a%b; m != 0; m = a%b) { a = b; b = m; } return b; } 分析:辗转相除法需要用到数值之间的取余运算,这是非常耗时间的. 思路二: 改进辗转相除法中取余(除法)运算,改为减法.这里

[经典面试题][网易]数组分割

[题目] 任意2N个正整数,从其中选出N个整数,使得选出的N个整数和同剩下的N个整数之和的差最小. [来源] 网易 [分析] 假设数组A[1..2N]所有元素的和是SUM.模仿动态规划解0-1背包问题的策略. 从2N个数中找N个元素,有三种可能:大于Sum/2,小于Sum/2以及等于Sum/2.而大于Sum/2与小于等于Sum/2没区别,故可以只考虑小于等于Sum/2的情况. 令S(k, i)表示前k个元素中任意i个元素的和的集合.显然: S(k, 1) = {A[i] | 1<= i <=

编程之美2.18 数组分割 原创解O(nlogn)的时间复杂度求解:

题目:有一个无序.元素个数为2n的正整数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近? 1 1 2 -> 1 1 vs  2 看题时,解法的时间复杂度一般都大于或等于O(n^2).突然灵感一闪,发现一个新的解法,应该算是一个动态规划的过程吧,思路比较简单,请看代码.空间复杂度O(nlogn),时间复杂度O(n).但是不能确定是否适用所有正整数组,如果有错,请给出你的测试用例,谢谢! 代码如下: 1 1 #include <iostream> 2 2 #

算法之--数组分割

题目来源:编程之美2.18 有一个无序的,元素个数为2n的正整数的数组,要求: 如何能把这个数组分割为元素个数为n的两个数组,使得两个子数组的和尽量接近. 解析:因为两个子数组的和是一定的,等于整个数组的和.现在要求使得两个字数组的和尽量的接近,也就意味着要从其中选出n个数使得这n个数的和尽可能的接近sum/2,不妨设为从小于sum/2的方向接近.于是,这就是一个01背包的问题: 现在有2N个物品,每个物品的重量为A[i],有一个背包的大小为sum/2,现在从中挑选出N个物品,使得背包尽可能的被