内容简介:交换数组元素,使得数组的和的差最小
问题描述
设有两个 长度相等的、数值类型的 无序序列a,b;
要求:通过交换a,b中的元素,使得[a中所有元素的和] 与 [b中所有元素的和] 的差最小。
求解思路
在逻辑层面将两个序列,看成一个序列。则问题可以转换成:从一个长度为2n的数组中,选出n个元素,使得这n个元素的和接近[两个数组的所有数据元素的和的 一半 ]。
因此可以通过动态规划来求解:
-
状态是
- offset:某一子阶段可以从[逻辑序列]的该偏移处开始取数据元素
- alreadySelectSize:在某一子阶段开始前,已经选择的元素的数量,该子阶段应该选择的元素的数量为 n - alreadySelectSize
- target:某一子阶段所选择的所有的元素的和需要接近的数值
-
状态转移方程是
dynamicProgramming(offset, alreadySelectSize, target) =
-
当
alreadySelectSize == n
时, 空序列 -
当
2n - offset + alreadySelectSize == n
时, 逻辑序列[offset...2n] - 否则, dynamicProgramming(offset+1, alreadySelectSize, target) 和 [逻辑序列[offset]] + dynamicProgramming(offset+1, alreadySelectSize+1, target-逻辑序列[offset])中所有元素的和更为接近target者
-
当
除了使用动态规划之外,还可以使用回溯算法求出所有可能的解,然后再从中选择最优的解。
代码实现
package cn.timd.DynamicProgramming; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Stack; public class MinDifference { private List<BigDecimal> firstList; private List<BigDecimal> secondList; private BigDecimal target; private int size; private int shouldSelectSize; public MinDifference(List<BigDecimal> firstList, List<BigDecimal> secondList) { if (firstList == null || secondList == null) throw new RuntimeException("firstList == null or secondList == null"); if (firstList.size() == 0 || firstList.size() != secondList.size()) throw new RuntimeException("invalid firstList or secondList"); this.firstList = firstList; this.secondList = secondList; target = getSum(firstList, secondList).divide(new BigDecimal(2)); // average size = firstList.size() + secondList.size(); shouldSelectSize = size / 2; } private BigDecimal getSum(List<BigDecimal>... lists) { BigDecimal sum = new BigDecimal(0); for (List<BigDecimal> list: lists) for (BigDecimal element: list) sum = sum.add(element); return sum; } private BigDecimal getTarget() { return target; } public int getSize() { return size; } public BigDecimal get(int n) { if (n < 0 || n >= getSize()) throw new RuntimeException("invalid index"); if (n < firstList.size()) return firstList.get(n); return secondList.get(n - firstList.size()); } public int getShouldSelectSize() { return shouldSelectSize; } private List<BigDecimal> dynamicProgramming(int offset, int alreadySelectSize, BigDecimal target) { List<BigDecimal> decision = new ArrayList<BigDecimal>(); if (getSize() - offset + alreadySelectSize == getShouldSelectSize()) { for (int n = offset; n < getSize(); ++n) decision.add(get(n)); return decision; } if (alreadySelectSize == getShouldSelectSize()) return decision; List<BigDecimal> notChooseOffset = dynamicProgramming(offset+1, alreadySelectSize, target); List<BigDecimal> chooseOffset = new ArrayList<BigDecimal>(); chooseOffset.add(get(offset)); chooseOffset.addAll(dynamicProgramming(offset+1, alreadySelectSize+1, target.subtract(get(offset)))); BigDecimal notChooseOffsetSum = getSum(notChooseOffset); BigDecimal chooseOffsetSum = getSum(chooseOffset); if (notChooseOffsetSum.subtract(target).abs().compareTo (chooseOffsetSum.subtract(target).abs()) < 0) return notChooseOffset; return chooseOffset; } public List<BigDecimal> dynamicProgramming() { return dynamicProgramming(0, 0, getTarget()); } private static class Node { BigDecimal element; List<Node> nextNodes; int offset = 0; Node(BigDecimal element) { this.element = element; } Node getNextNode() { if (nextNodes == null || offset >= nextNodes.size()) return null; return nextNodes.get(offset++); } void clear() { offset = 0; } } private Node createNodes() { Node root = new Node(null); List<Node> nodes = new ArrayList<Node>(); for (int i = 0; i < getSize(); ++i) nodes.add(new Node(get(i))); root.nextNodes = nodes; for (int i = 0; i < getSize() - 1; i++) nodes.get(i).nextNodes = nodes.subList(i+1, nodes.size()); return root; } public List<BigDecimal> backTrace() { BigDecimal totalSum = getSum(firstList, secondList); List<BigDecimal> result = null; BigDecimal currentAbs = null; Node root = createNodes(); Stack<Node> stack = new Stack<Node>(); stack.push(root); Node extensionNode, nextNode; while (!stack.empty()) { extensionNode = stack.peek(); nextNode = extensionNode.getNextNode(); if (nextNode == null) { extensionNode = stack.pop(); if (stack.empty()) break; extensionNode.clear(); } else { if (stack.size() == getShouldSelectSize()) { List<BigDecimal> list = new ArrayList<BigDecimal>(); for (Node node: stack) if (node != root) list.add(node.element); list.add(nextNode.element); BigDecimal abs = getSum(list).multiply(new BigDecimal(2)).subtract(totalSum).abs(); if (currentAbs == null || abs.compareTo(currentAbs) < 0) { result = list; currentAbs = abs; } } else stack.push(nextNode); } } return result; } public static void test(String[] args) { int[] firstArray = new int[]{12, 19, 8, 3, 16, 13, 4, 7, 1, 2}; int[] secondArray = new int[]{10, 6, 18, 5, 11, 17, 9, 14, 20, 15}; List<BigDecimal> firstList = new ArrayList<BigDecimal>(); List<BigDecimal> secondList = new ArrayList<BigDecimal>(); for (int element: firstArray) firstList.add(new BigDecimal(element)); for (int element: secondArray) secondList.add(new BigDecimal(element)); MinDifference minDifference = new MinDifference(firstList, secondList); for (BigDecimal element: minDifference.backTrace()) System.out.print(element.toString() + " "); System.out.println(); for (BigDecimal element: minDifference.dynamicProgramming()) System.out.print(element.toString() + " "); System.out.println(); } public static void main(String[] args) { test(args); } }
执行结果:
文档
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- LeetCode-数组-删除元素
- 求非负元素数组所有元素能组合的最大字符串
- 前端通关日记之优雅添加数组元素
- [译] 对元素持有弱引用的 Swift 数组
- [译] 对元素持有弱引用的 Swift 数组
- PHP删除数组中指定下标的元素方法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
社交网站界面设计
Christian Crumlish、Erin Malone / 樊旺斌、师蓉 / 机械工业出版社 / 2010-9-1 / 69.00元
《社交网站界面设计》提供100多种模式、原则以及最佳实践,并针对在设计社交网站时经常遇到的问题给出明确建议。本书将提供给你培养用户交互习惯和构建社区最具价值的参考。 本书作者将与你分享难得的经验,教会你平衡各种不同的因素,并与你的用户共同构建和谐健康的网络社区。 本书教会你 掌握创建任何网站时都会用到的原则 学习基本设计模式,以便向现有的网站中添加新的社交组件 学会在......一起来看看 《社交网站界面设计》 这本书的介绍吧!