编程之美:数组分割

题目概述:有一个没有排序,元素个数为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最接近的那个和,这便是答案。
因为这个过程中只关注和不大于SUM/2的那个子数组的和。所以集合中重复的和以及大于SUM/2的和都是没有意义的。把这些没有意义的和剔除掉,剩下的有意义的和的个数最多就是SUM/2个。所以,我们不需要记录S(2N,N)中都有哪些和,只需要从SUM/2到1遍历一次,逐个询问这个值是不是在S(2N,N)中出现,第一个出现的值就是答案。

例子:

比如说有一个6个数的数组,分成两个数组,其中每个数组有3个数:

[2,5,7,9,11,13]

计算可得:SUM=47,SUM/2=23.5

我们需要求的是:

k=6,i=3时,套入上面公式可得:

S(6,3)=S(5,3) U {13+{S(5,2)}}

集合S(5,3)={14,16,18,20,22,21,23,25,27}

集合S(5,2)={7,9,11,12,13,14,16,18,20}

13+S(5,2)={20,22,24,25,26,27,29,31,33}

最后:

S(6,3)={14,16,18,20,21,22,23,24,25,26,27,29,31,33}

我们在集合S(6,3)中,从SUM/2(23.5)到1遍历一次,逐个询问这个值是不是在S(6,3)中出现,第一个出现的值就是答案,也就是23,即答案。

public class Main {  

    public static void main(String[] args) {
        int A[] = { 1, 2, 3, 5, 7, 8, 9 };
        // int A[] = { 1, 5, 7, 8, 9, 6, 3, 11, 20, 17 };
        func(A);
    }  

    static void func(int A[]) {
        int i;
        int j;  

        int n2 = A.length;
        int n = n2 / 2;
        int sum = 0;
        for (i = 0; i < A.length; i++) {
            sum += A[i];
        }  

        /**
         * flag[i][j]:任意i个数组元素之和是j,则flag[i][j]为true
         */
        boolean flag[][] = new boolean[A.length + 1][sum / 2 + 1];
        for (i = 0; i < A.length; i++)
            for (j = 0; j < sum / 2 + 1; j++)
                flag[i][j] = false;  

        flag[0][0] = true;  

        for (int k = 0; k < n2; k++) {
            for (i = k > n ? n : k; i >= 1; i--) {
                // 两层外循环是遍历集合S(k,i)
                for (j = 0; j <= sum / 2; j++) {
                    if (j >= A[k] && flag[i - 1][j - A[k]])
                        flag[i][j] = true;
                }
            }
        }
        for (i = sum / 2; i >= 0; i--) {
            if (flag[n][i]) {
                System.out.println("sum is " + sum);
                System.out.println("sum/2 is " + sum / 2);
                System.out.println("i is " + i);
                System.out.println("minimum delta is " + Math.abs(2 * i - sum));
                break;
            }
        }
    }
}  
时间: 2024-11-05 16:33:17

编程之美:数组分割的相关文章

编程之美——数组分割

一.题目概述:有一个没有排序,元素个数为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) }按照这个递推公式来

编程之美-数组中最长递增子序列(包括输出)

#include <iostream> #define N 8 using namespace std; int main(){ int a[N]={1,-1,2,-3,4,-5,6,-7}; int lis[N]; int result[N];//结果 for(int i=0;i<N;i++) result[i]=0; for(int i=0;i<N;i++) { lis[i]=1; for (int j=0;j<i; j++) { if( a[i]> a[j] &a

【编程之美】数组分割

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

编程之美之2.14 求数组的子数组之和的最大值

[题目] 一个有N个整数元素的一维数组(A[0],A[1],A[2],...A[n-1]),这个数组中当然有很多子数组,那么子数组之和的最大值是多少? 该子数组是连续的. 我们先来明确一下题意: (1)子数组意味着是连续的. (2)题目只需要求和,并不需要返回子数组的具体位置. (3)数组的元素是整数,所以数组可能包含正整数,负整数或者零. 举几个例子: 数组:[1,-2,3,5,-3,2]返回8 数组:[0,-2,3,5,-1,2]返回9 数组:[-9,-2,-3,-5,-3]返回8 [解法一

[编程之美] 2.14 求数组的子数组之和的最大值

问题描述:给定一个包含N个整数的数组,求数组的子数组之和的最大值. 这是递归和贪心策略的一个经典问题.现在,对这个问题进行一下总结. 1 明确题意 题目中的子数组要求是连续的,也就是数组中的某个连续部分. 如果数组中都是正整数,直接相加就行.因此,主要是要考虑负数的情况. 2 直接求所有的子数组和 最简单且容易理解的解法是求出所有的子数组和,然后保存最大的和. int MaxSum(int *A, int n) { int maximum = -INF; int sum = 0; int 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的两个数组,并使两个子数组的和最接近. 基本思想: 假设数组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) }

数组分割问题(转)

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

编程之美之2.5 寻找最大的K个数

[题目] 有很多无序的数,从中找出最大的K个数.假定他们都不相等. [解法一] 如果数据不是很多,例如在几千个左右,我们可以排一下序,从中找出最大的K个数.排序可以选择快速排序或者堆排序 [cpp] view plaincopy #include<stdio.h> #include<stdlib.h> int cmp(const void *a,const void *b){ return *(int *)a - *(int *)b; } int main(){ int n,k;

【编程之美】目录

第1章  游戏之乐——游戏中碰到的题目 1.1 让CPU占用率听你的指挥 1.2 中国象棋将帅问题 1.3 一摞烙饼的排序 1.4 买书问题 第2章  数字之魅——数字中的技巧 2.1 求二进制中1的个数 2.2 不要被阶乘吓倒 2.3 寻找发帖"水王" 2.4 1的数目 2.5 寻找最大的K个数 2.6 精确表达浮点数 2.7 最大公约数问题 2.8 找符合条件的整数 2.9 斐波那契(Fibonacci)数列 2.10 寻找数组中的最大值和最小值 2.11 寻找最近点对 2.12