两个数组之和的问题

    技术2022-05-19  19

    问题1.任意两个相同长度的整形数组,通过交换数组中的若干个元素(交换不改变数组数组长度),使两个数组的和相差最小

    问题2.任意一个整形数组,将其划分成两个子数组,要求两个子数组的和相差最小

     

     

    对问题1,贪心解似乎更加容易被采纳,即通过多次迭代,每次迭代都从两个数组中各选出一个数,使得两数组的和之差降低最快,但是贪心法看重每次降低最快,却无法覆盖可能几个数组合起来交换的到更优值的情况,所以,还是要尝试动态规划。两个数组元素个数相同,最多交换1/2个元素即可,比如,数组长度为6,则交换4个元素与交换2个元素的差值的绝对值是一样的,所以动态规划上限为1/2元素总数。

     

    转换方程状态不是很好想,我想到的是用bit位表示选择情况,两个数组各有相应长度的bit位来记录选择情况,然后每种bit组合对应一个数组和的差值。在某个中间状态时,尝试在前一个状态的某个组合上加入新的pair表示交换元素配对,如果配对能降低差值,则有效,否则无效,迭代的整个过程中随时更新当前最优组合。

     

    初始化dp[0]为全0的bit位,并且对应的差值为两数组的和之差的最初值,在迭代过程中,选中了A数组和B数组的m和n元素交换时,{新差值}={原差值}+2*(B[n]-A[m])。

     

    @SuppressWarnings("unchecked") public static void swapToMinSumDelta() { int[] A = new int[]{99,49,1,212,3,100}; int[] B = new int[]{1,2,233,41,5,40}; Map<BitSet, Integer>[] dp = new Map[A.length/2+1]; for(int i=0; i<dp.length; i++) dp[i] = new HashMap<BitSet, Integer>(); int S1=0, S2=0; for(int i=0; i<A.length; i++) S1 += A[i]; for(int i=0; i<B.length; i++) S2 += B[i]; int length = A.length+B.length; BitSet minSet = new BitSet(length); int min = Math.abs(S1-S2); dp[0].put(minSet, new Integer(S1-S2)); for(int i=1; i<dp.length; i++) { Iterator<BitSet> it = dp[i-1].keySet().iterator(); while(it.hasNext()) { BitSet bitSet = it.next(); for(int m=0; m<A.length; m++) if(!bitSet.get(m+B.length)) for(int n=0; n<B.length; n++) if(!bitSet.get(n)) { BitSet iBitSet = bitSet.get(0, length); iBitSet.set(m+B.length); iBitSet.set(n); if(!dp[i].containsKey(iBitSet)) { //(S1-A[m]+B[n])-(S2-B[n]+A[m])=(S1-S2)+2*(B[n]-A[m]) int a = dp[i-1].get(bitSet); int b = dp[i-1].get(bitSet)+2*(B[n]-A[m]); if(Math.abs(a)>=Math.abs(b)) dp[i].put(iBitSet, b); if(min>Math.abs(b)) { min = Math.abs(b); minSet = iBitSet; } } } } } // Print result List<Integer> a = new ArrayList<Integer>(); for(int i=0; i<A.length; i++) if(!minSet.get(i+B.length)) a.add(A[i]); for(int i=0; i<B.length; i++) if(minSet.get(i)) a.add(B[i]); List<Integer> b = new ArrayList<Integer>(); for(int i=0; i<B.length; i++) if(!minSet.get(i)) b.add(B[i]); for(int i=0; i<A.length; i++) if(minSet.get(i+B.length)) b.add(A[i]); System.out.print("Minimal is " + min + ", result group is " + a + " vs " + b); }

     

     

     

    对问题2,类似的也可以用bit位作状态记录,初始值取Sum/2,然后迭代选择将差值的绝对值减小的不同组合,取最优解。

     

    @SuppressWarnings("unchecked") public static void partitionToMinSumDelta() { int[] A = new int[]{1,2,3,4,5,6,1,432,43,453,32,21}; Map<BitSet, Double>[] dp = new Map[A.length+1]; for(int i=0; i<dp.length; i++) dp[i] = new HashMap<BitSet, Double>(); int S=0; for(int i=0; i<A.length; i++) S += A[i]; double min = S/2.0; BitSet minSet = new BitSet(A.length); dp[0].put(minSet, new Double(S/2.0)); for(int i=1; i<dp.length; i++) { Iterator<BitSet> it = dp[i-1].keySet().iterator(); while(it.hasNext()) { BitSet set = it.next(); for(int k=0; k<A.length; k++) if(!set.get(k)) { BitSet iSet = set.get(0, A.length); iSet.set(k); double a = dp[i-1].get(set); double b = dp[i-1].get(set) - A[k]; if(Math.abs(a)>=Math.abs(b)) dp[i].put(iSet, b); if(min>Math.abs(b)) { min = Math.abs(b); minSet = iSet; } } } } // Print result System.out.print("Minimal is " + (min*2) + ", result array group is : { "); for(int i=0; i<A.length; i++) if(minSet.get(i)) System.out.print(A[i]+" "); System.out.print("} vs { "); for(int i=0; i<A.length; i++) if(!minSet.get(i)) System.out.print(A[i]+" "); System.out.print("}"); }

     


    最新回复(0)