编程之美:第二章 数字之魅 2.18数组分割

来源:互联网 发布:明道办公软件登录 编辑:程序博客网 时间:2024/05/17 06:19
/*数组分割:有一个无序、元素个数为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不能超过1092 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*n4 for(int i = min(k,n); i >= 1 ; i--)//内阶段i,从这个k个数中任意选出i个数,i = 1,2,...,k5 for(int v = 1 ; v <= iSum/2 ; v++)//状态s,有两个决策包含或不包含元素k。状态:对这i个数的和为s,s = 1,2,...,sum/26 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,注意最大和不能超过1000int 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不能超过109memset(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;}

0 0
原创粉丝点击