/*
数组分割:
有一个无序、元素个数为2*n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近?
分析:
题目本质是要从2n个整数中找出n个,使得他们的和尽可能地靠近所有整数之和的一半。
解法1:
先将数组的所有元素排序为:a1<a2<...<a2n
将它们划分为两个子数组S1 = [a1,a3,...,a2n-1]和S2 = [a2,a4,a6,...,a2n]
从S1和S2中找出一对数进行交换,使得SUM(S1)和SUM(S2)之间的差值尽可能的小,知道找不到可对换的。这种想法的缺陷是S1和S2并不是最优的。
解法2:
假设2n个整数之和为SUM。从2n个整数中找出n个元素的和,有三种可能:大于SUM/2,等于SUM/2,和小于SUM/2.
只可以考虑<=SUM/2的情况。
用动态规划解决该问题,可以把任务分成2*N步,第k步的定义是前k个元素中任意i个元素的和,所有可能的取值之集合为Sk(只考虑<=SUM/2的情况)
然后将第k步拆分成两个小步,即首先得到前k-1个元素中,任意i割元素,总共能有多少种取值,设这个取值集合为Sk-1 = {Vi},第2步就是令
Sk = Sk-1 U {Vi + arr[k]},即可完成第k步。
时间复杂度是O(2^N)
解法3:
解法二的拆分方法需要遍历Sk-1 = {Vi}的元素,由于Sk-1 = {Vi}的元素个数随着k的增大而增大。
原来是给定Sk-1 = {Vi},求Sk,能不能给定Sk的可能值v和arr[k],去寻找v- arr[k]是否在Sk-1={Vi}中呢?由于Sk可能值的集合大小与k无关,所以
这样设计的动态规划算法其第k步的时间复杂度与k无关。
输入:
5(元素个数的一半)
1 3 11 8 20 5 7 9 6 17
输出:(两个子数组之间的最小差值)
1
分析
1 3 11 8 20(43)
5 7 9 6 17(44)
*/
/*
关键:
1 bool dp[109][100];//isOK[i][v]表示是否可以找到i个数,使得他们的和是v,注意最大和不能超过100,2*n不能超过109
2 dp[0][0] = true;//可以找到0个数,使得他们的和是0。dp[i][v]:从前i个数中取任意k个数,这些数之和为v的取法是否存在
3 for(int k = 1 ; k <= 2 * n ; k++)//时间复杂度为O(N^2*Sum),外阶段k,在前k个数中进行选择,k = 1,2,...,2*n
4 for(int i = min(k,n); i >= 1 ; i--)//内阶段i,从这个k个数中任意选出i个数,i = 1,2,...,k
5 for(int v = 1 ; v <= iSum/2 ; v++)//状态s,有两个决策包含或不包含元素k。状态:对这i个数的和为s,s = 1,2,...,sum/2
6 if(v >= pArr[k] && dp[i-1][ v - pArr[k] ])//决策:决定这i个数的和有两种决策,一个是这i个树种包含第k个数,另一个是不包含第k个数
*/
#include <stdio.h>
#include <string.h>
const int MAXSIZE = 10000;
//bool dp[109][100];//isOK[i][v]表示是否可以找到i个数,使得他们的和是v,注意最大和不能超过1000
int min(int a,int b)
{
return a < b ? a : b;
}
void splitArray(int* pArr,int n,int iSum)
{
bool dp[109][100];//isOK[i][v]表示是否可以找到i个数,使得他们的和是v,注意最大和不能超过100,2*n不能超过109
memset(dp,0,sizeof(dp));
dp[0][0] = true;//可以找到0个数,使得他们的和是0。dp[i][v]:从前i个数中取任意k个数,这些数之和为v的取法是否存在
for(int k = 1 ; k <= 2 * n ; k++)//时间复杂度为O(N^2*Sum),外阶段k,在前k个数中进行选择,k = 1,2,...,2*n
{
for(int i = min(k,n); i >= 1 ; i--)//内阶段i,从这个k个数中任意选出i个数,i = 1,2,...,k
{
for(int v = 1 ; v <= iSum/2 ; v++)//状态s,有两个决策包含或不包含元素k。状态:对这i个数的和为s,s = 1,2,...,sum/2
{
if(v >= pArr[k] && dp[i-1][ v - pArr[k] ])//决策:决定这i个数的和有两种决策,一个是这i个树种包含第k个数,另一个是不包含第k个数
{
dp[i][v] = true;
}
}
}
}
int s;
for(s = iSum/2 ; s >=1 && !dp[n][s] ;s--);//因为最终是n个元素,确定最接近的给定值sum/2的和
printf("%d\n",iSum - 2*s);
}
void process()
{
int n;;
while(EOF != scanf("%d",&n))
{
if(n <= 0 || 2*n > MAXSIZE)
{
continue;
}
int iArr[MAXSIZE];
int iSum = 0;
for(int i = 1 ; i <= 2 * n ; i++)
{
scanf("%d",&iArr[i]);
iSum += iArr[i];
}
splitArray(iArr,n,iSum);
}
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}
编程之美:第二章 数字之魅 2.18数组分割
最新推荐文章于 2018-04-24 19:12:30 发布