`

【编程之美】数组分割问题

 
阅读更多

一,问题:

1. 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为两个子数组,子数组的元素个数不限,并使两个子数组之和最接近。

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

二,分析:

假设数组A[1..2N]所有元素的和是SUM。模仿动态规划解0-1背包问题的策略,令S(k, i)表示前k个元素中任意i个元素的和的集合。显然:
S(k, 1) = {A[i] | 1<= i <= k}
S(k, k) = {A[1]+A[2]+…+A[k]}
S(k, i) = S(k-1, i) U {A[k] + x | x属于S(k-1, i-1) }
按照这个递推公式来计算,最后找出集合S(2N, N)中与SUM最接近的那个和,这便是答案。这个算法的时间复杂度是O(2^N).
因为这个过程中只关注和不大于SUM/2的那个子数组的和。所以集合中重复的和以及大于SUM/2的和都是没有意义的。把这些没有意义的和剔除掉,剩下的有意义的和的个数最多就是SUM/2个。所以,我们不需要记录S(2N,N)中都有哪些和,只需要从SUM/2到1遍历一次,逐个询问这个值是不是在S(2N,N)中出现,第一个出现的值就是答案。我们的程序不需要按照上述递推公式计算每个集合,只需要为每个集合设一个标志数组,标记SUM/2到1这个区间中的哪些值可以被计算出来。

二,解法:

由于对两个子数组和最接近的判断不太直观,我们需要对题目进行适当转化。我们知道当一个子数组之和最接近原数组之和sum的一半时,两个子数组之和是最接近的。所以转化后的题目是:从2n个数中选出任意个数,其和尽量接近于给定值sum/2


这个问题存储的是从前k个数中选取任意个数,且其和为s的取法是否存在dp[k][s]。之所以将选出的数之和放在下标中,而不是作为dp[k]的值,是因为那种做法不满足动态规划的前提——最优化原理,假设我们找到最优解有k个数p1p2...pk(选出的这k个数之和是最接近sum/2的),但最优解的前k-1个数p1p2...pk-1之和可能并不是最接近sum/2的,也就是说可能在访问到pk之前有另一组数q1q2....qk-1其和相比p1p2...pk-1之和会更接近sum/2,即最优解的子问题并不是最优的,所以不满足最优化原理。因此我们需要将dp[k]的值作为下标存储起来,将这个最优问题转化为判定问题,用带动态规划的思想的递推法来解。


外阶段:在前k1个数中进行选择,k1=1,2...2*n。
内阶段:从这k1个数中任意选出k2个数,k2=1,2...k1。

状态:这k2个数的和为s,s=1,2...sum/2。

决策:决定这k2个数的和有两种决策,一个是这k2个数中包含第k1个数,另一个是不包含第k1个数。
dp[k][s]表示从前k个数中取任意个数,且这些数之和为s的取法是否存在。

#include <iostream>
#include <algorithm>

using namespace std;

#define MAXN 101
#define MAXSUM 100000
int A[MAXN];
bool dp[MAXN][MAXSUM];

// dp[k][s]表示从前k个数中去任意个数,且这些数之和为s的取法是否存在
int main()
{
	int n, i, k1, k2, s, u;
	cin >> n;
	for (i=1; i<=2*n; i++)
		cin >> A[i];
	int sum = 0;
	for (i=1; i<=2*n; i++)
		sum += A[i];
	memset(dp,0,sizeof(dp));
	dp[0][0]=true;
	// 外阶段k1表示第k1个数,内阶段k2表示选取数的个数
	for (k1=1; k1<=2*n; k1++)			// 外阶段k1
	{
		for (k2=k1; k2>=1; k2--)		// 内阶段k2
			for (s=1; s<=sum/2; s++)	// 状态s
			{
				//dp[k1][s] = dp[k1-1][s];
				// 有两个决策包含或不包含元素k1
				if (s>=A[k1] && dp[k2-1][s-A[k1]])
					dp[k2][s] = true;
			}
	}
	// 之前的dp[k][s]表示从前k个数中取任意k个数,经过下面的步骤后
	// 即表示从前k个数中取任意个数
	for (k1=2; k1<=2*n; k1++)
		for (s=1; s<=sum/2; s++)
			if (dp[k1-1][s])
			    dp[k1][s]=true;
	// 确定最接近的给定值sum/2的和
	for (s=sum/2; s>=1 && !dp[2*n][s]; s--)
	           ;
	           
    printf("the differece between two sub array is %d\n", sum-2*s);
}

2. 解法:

但本题还增加了一个限制条件,即选出的物体数必须为n,这个条件限制了内阶段k2的取值范围,并且dp[k][s]的含义也发生变化。这里的dp[k][s]表示从前k个数中取任意不超过n的k个数,且这些数之和为s的取法是否存在

#include <iostream>
#include <algorithm>

using namespace std;

#define MAXN 101
#define MAXSUM 100000
int A[MAXN];
bool dp[MAXN][MAXSUM];

// 题目可转换为从2n个数中选出n个数,其和尽量接近于给定值sum/2
int main()
{
	int n, i, k1, k2, s, u;
	cin >> n;
	for (i=1; i<=2*n; i++)
		cin >> A[i];
	int sum = 0;
	for (i=1; i<=2*n; i++)
		sum += A[i];
	memset(dp,0,sizeof(dp));
	dp[0][0]=true;
	// 对于dp[k][s]要进行u次决策,由于阶段k的选择受到决策的限制,
	// 这里决策选择不允许重复,但阶段可以重复,比较特别
	for (k1=1; k1<=2*n; k1++)				// 外阶段k1
		for (k2=min(k1,n); k2>=1; k2--)		// 内阶段k2
			for (s=1; s<=sum/2; s++)	// 状态s
				// 有两个决策包含或不包含元素k1
				if (s>=A[k1] && dp[k2-1][s-A[k1]])
					dp[k2][s] = true;
	// 确定最接近的给定值sum/2的和
	for (s=sum/2; s>=1 && !dp[n][s]; s--);
	printf("the differece between two sub array is %d\n", sum-2*s);
}


注意:如果数组中有负数的话,上面的背包策略就不能使用了(因为第三重循环中的s是作为数组的下标的,不能出现负数的),需要将数组中的所有数组都加上最小的那个负数的绝对值,将数组中的元素全部都增加一定的范围,全部转化为正数,然后再使用上面的背包策略就可以解决了。




分享到:
评论

相关推荐

    数组分割1

    该问题源于《编程之美》中的2.18章节,同时也是July的《微软等公司面试100题》中的32题,主要探讨如何通过交换两个序列的元素,使得两个序列元素之和的差最小。这个问题的关键在于找到一种有效的交换策略,使得两个...

    编程题刷的题库

    这些题目来自于一家著名的通信设备制造商,通常用于校园招聘的编程测试,旨在评估应聘者的编程能力、逻辑思维和问题解决技巧。下面将逐一分析这些题目,介绍相关知识点。 1. **华为 - 坐标.c** 这个题目可能涉及到...

    【顿开教育】C语言美女拼图游戏

    游戏的核心在于使用C语言的控制结构(如if语句、for循环和while循环)以及数组和指针等概念,来实现图片的分割、显示、移动和重组。在编写代码的过程中,开发者需要思考如何正确处理图像数据,如何实现用户交互,...

    爱心源码-讲解numpy包相关数组在实际应用中的使用方法

    - 数组拆分:`np.split()`、`np.array_split()`和`np.hsplit()`等函数可以将数组分割成多个子数组。 4. **数学与统计操作**: - 基础数学运算:如加法、减法、乘法、除法等,可以对整个数组进行。 - 算术统计:...

    美女拼图游戏

    【美女拼图游戏】是一款专为初级开发者设计的源码项目,旨在帮助他们了解和...通过学习这个【美女拼图游戏】的源码,初学者不仅能掌握游戏开发的基本流程,还能锻炼解决问题的能力,为未来更复杂的项目打下坚实基础。

    因数与倍数PPT模板:数学之美.pptx

    5. 计算机科学:在编程中,数组的大小通常是2的幂,利用因数和倍数的性质可以优化数据结构和算法。6. 环境科学:在环境科学中,生态系统的能量流动和物质循环也涉及因数和倍数的概念。结论因数与倍数是数学中的基础...

    Android游戏源码安卓美女拼图游戏

    《Android游戏源码安卓美女拼图游戏》是一个适合Android初学者和高级开发者研究的项目,它揭示了...通过深入研究源码,你可以了解到一个完整的游戏项目是如何从概念到实现,从而提高你的编程素养和解决问题的能力。

    n阶幻方的C#实现

    1. 对于更深入的理论背景,可参考《数学之美》一书中关于幻方的章节。 2. 在线资源,如维基百科的幻方词条,提供了更多关于幻方历史和构造方法的信息。 3. 计算机科学课程,如数据结构和算法,也经常包含幻方的编程...

    webgl编程指南及源码2/2

    文件太大,分割成两个文件了。 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: (美)Kouichi Matsuda Rodger Lea(松田...

    webgl编程指南及源码1/2

    文件太大,分割成两个文件了。 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: (美)Kouichi Matsuda Rodger Lea(松田...

    MFC制作美女拼图

    在程序启动时,我们可以读取美女图片,将其分割成多个小块,并保存在内存中。这个过程可能涉及到图像处理技术,例如使用OpenCV库进行图像切割。每个小块的信息,包括其原始位置和当前位置,可以存储在一个结构体数组...

    面试算法整理

    题目来源于《剑指Offer》这本书中的第29题,同时参考了《编程之美》中关于寻找“发帖水王”的问题。这两个来源均提供了不同的解题思路和方法论。 **解法一:基于快排中分割算法的方法** 1. **基本思想**: - 通过...

    IOI国家集训队论文集1999-2019

    骆 骥 -《浅析解 "对策问题" 的两种思路——从《取石子》问题谈起》 孙方成 -《偶图的算法及应用》 孙林春 -《让我们做得更好——从《Parity》的解法谈程序的优化》 王知昆 -《搜索顺序的选择》 许智磊 -《二分...

    C语言编程练习题

    本资源整理了 32 个 C 语言编程练习题,涵盖了基本的数据类型、运算符、控制结构、函数和数组等内容。这些练习题旨在帮助初学者熟悉 C 语言的基本概念和编程技巧,并为更深入的学习和实践打下基础。 1. float 类型...

    Android小游戏美女拼图实现点击选择再点击互换位置实现拼图过关;可以做为一个验证码.rar

    为了实现拼图效果,我们需要预先准备完整的美女图片,然后将其分割成多个小块。这通常通过图像处理库如Picasso或Glide来完成,它们可以帮助我们加载和裁剪图片。 其次,触摸事件处理是游戏交互的关键。在Android中...

    ACM培训资料(练就坚实的基础)

    ### ACM培训资料(练就坚实的基础) #### 一、ACM国际大学生程序...通过持续的学习和实践,不断提高自己的编程技能和解决问题的能力,相信每位参与者都能够在这条路上越走越远。未来属于那些不断努力、勇往直前的人们。

    PHP经典实例.(美)斯克拉&切贝特伯格

    本书《PHP经典实例》由(美)斯克拉和切贝特伯格编写,是一本针对PHP编程语言的实用教材。PHP(Hypertext Preprocessor,原名:Personal Home Page)是一种广泛使用的开源服务器端脚本语言,非常适合网页开发,能够...

    迈进算法世界的大门

    以归并排序为例,该算法遵循分治法的思路,首先将原始数组不断分割直至每个子数组仅含单个元素,随后两两合并,逐步构建出完全排序的数组。这一过程既体现了算法的优雅,又展示了分治法在提高效率方面的显著作用。 ...

    源代码_拼图游戏.rar

    分割图像的过程就是将这个数组切分成多个子数组,每个子数组代表一块拼图。这一步可能需要用到矩阵操作或图像处理库,如OpenCV。 然后,我们需要设计拼图的移动和旋转算法。在二维平面上,拼图块的移动可以通过交换...

    vc mfc 分割字符串

    在编程中,字符串分割是指根据指定的分隔符(如逗号、空格或其他字符)将一个完整的字符串拆分成多个子字符串的过程。这种操作通常用于解析文本数据,例如将CSV文件中的记录转换为数组或列表。 #### 二、MFC中的...

Global site tag (gtag.js) - Google Analytics