[编程之美] PSet2.18 数组分割

来源:互联网 发布:linux支持的处理器 编辑:程序博客网 时间:2024/05/22 05:17
参考:http://blog.csdn.net/linyunzju/article/details/7729774

问题描述:

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


分析与思路:

        从题目中可以看出,题目的本质就是从2n个整数中找出n个,使得它们的和尽可能靠近所有整数之和的一半。

解法一:

       这个问题存储的是从前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]的值作为下标存储起来,将这个最优问题转化为判定问题,用带动态规划的思想的递推法来解。

         代码如下:

//数组分割:(2n个数组中分成两半让各组之和最接近)//使用dp[i][s]来标记能否找到i个数,使他们之和为s//递推过程为dp[i][s]=dp[i-1][s]或dp[i-1][s-Arr[i]],复杂度O(N^2*Sum)#define MIN(a,b) ((a)>(b)?(b):(a))const int MAXLEN = 100;const int MAXSUM = 1000;int ArrPartition(int Arr[] , int arrLen ){//---判断输入数组是否为偶数大小if(arrLen & 1)return -1;//---对数组求和int sum = 0;for(int i=0 ; i<arrLen ; i++)sum += Arr[i];//---定义判决数组dpbool dp[MAXLEN][MAXSUM];memset(dp,0,sizeof(dp));dp[0][0] = true;//---动态规划求解判决数组for(int i=1 ; i<=arrLen ; i++){//对于第i个数for(int j=MIN(i,arrLen/2) ;j>=1; j--)//i个数中取j个数,j的范围为[i,arrLen/2],由递推公式可知要自底向上求解for(int v=1 ; v<=sum/2 ; v++){if(v>=Arr[i-1] && dp[j-1][v-Arr[i-1]])//Arr下标从0开始,与dp下标从1开始不同。dp[j][v] = true;}}int s;for (s=sum/2; s>=1 ; s--)if(dp[arrLen/2][s])break;return sum/2-s;}

0 0
原创粉丝点击