zoukankan      html  css  js  c++  java
  • 编程之美:数组分割

    题目概述:有一个没有排序,元素个数为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;  
                }  
            }  
        }  
    }  
  • 相关阅读:
    某大神C#框架后台发送信息的查找及破解
    多平台下Modbus通信协议库的设计(一)
    wpf 窗口程序下将datagrid导出为excel
    luogu P2598 [ZJOI2009]狼和羊的故事 |网络流最小割
    luogu P3171 [CQOI2015]网络吞吐量 |网络流最大流
    luogu P2469 [SDOI2010]星际竞速 |网络流费用流
    luogu P2172 [国家集训队]部落战争 |网络流最少路径覆盖
    luogu P2045 方格取数加强版 |最大费用最大流
    luogu P6327 区间加区间sin和 |线段树
    luogu P2402 奶牛隐藏 |网络流最大流+二分
  • 原文地址:https://www.cnblogs.com/Donnnnnn/p/5722750.html
Copyright © 2011-2022 走看看