明天遇到了一个这样的问题
一个序列分成 n 段,要求各段和的差值的最大值最小
或者说,要求各段和尽可能靠近序列和 S / n
须要最优解,思路或者代码都行
理论利用场景:布局瀑布流排版
如果没有利用场景,其实这个问题还是比较复杂的,可能会波及到一些冀望预测类的货色
不过这里题主提供了利用场景,这个利用场景有一些特色,可能简化这个问题
特色1:序列中的每个元素差别不会太大,不会呈现 >> 或 << 的状况
特色2:序列个数不会太小,不会呈现边缘的极限状况
特色3:段数不会太大
特色4:是能够承受肯定的误差的
基于这些特色,一个简略的解题思路就有了
序列排序,从大到小往N个桶外面放,每次找桶内和最小的那个桶放以后值,循环至最初
数量越多误差越小
接下来上demo代码
public class MasonryLayout { // 初始化一个数字队列 List<Integer> initData; // 记录分几个组 int slotCount; class Node { int position; int weight; @Override public String toString() { return "Node{" + "position=" + position + ", weight=" + weight + '}'; } public String print() { return "{" + position + ", " + weight + '}'; } } class Sums { int sum; List<Node> nodeList; public Sums(int sum) { this.sum = sum; nodeList = new ArrayList<>(); } public void addNode(Node node) { nodeList.add(node); sum += node.weight; } @Override public String toString() { return "Sums{" + "sum=" + sum + '}'; } public String print() { // 按地位升序从新排一下 nodeList.sort(Comparator.comparingInt(o -> o.position)); return "SUM: " + sum + " Nodes: " + nodeList.stream().map(node -> node.print()).collect(Collectors.joining("-")); } } public MasonryLayout(int basic, int length, int slotCount) { // 这里只是造模仿数据的代码 initData = new ArrayList<>(); Random random = new Random(); int seed = random.nextInt(basic) + basic; System.out.println("seed: " + seed); for (int i = 0; i < length; i++) { int num = random.nextInt(basic / 2) + seed; initData.add(num); } this.slotCount = slotCount; System.out.println("原始数据: " + initData); } void execute() { // 保留排序后的节点 List<Node> sortData = new ArrayList<>(); TreeSet<Sums> sumSet = makeTreeSet(); for (int i = 0; i < slotCount; i++) { sumSet.add(new Sums(0)); } for (int i = 0; i < initData.size(); i++) { Node node = new Node(); node.position = i; node.weight = initData.get(i); sortData.add(node); } // 降序所有Node,这里要从大到小取数 sortData.sort((o1, o2) -> o2.weight - o1.weight); System.out.println(sortData); for (Node node : sortData) { sumSet.first().addNode(node); TreeSet<Sums> sumSet1 = sumSet; sumSet = makeTreeSet(); sumSet.addAll(sumSet1); } sumSet.forEach(sums -> { System.out.println("分段: " + sums.print()); }); } TreeSet<Sums> makeTreeSet() { return new TreeSet<>(new Comparator<Sums>() { @Override public int compare(Sums o1, Sums o2) { // 升序,这里要取最小值 int ret = o1.sum - o2.sum; if (ret == 0) { return o1.hashCode() - o2.hashCode(); } else { return ret; } } }); } public static void main(String[] args) { MasonryLayout masonryLayout = new MasonryLayout(20, 60, 4); masonryLayout.execute(); }}
执行一下
后果
原始数据: [44, 36, 43, 41, 40, 44, 40, 40, 37, 40, 36, 39, 43, 36, 41, 39, 36, 36, 44, 44, 38, 41, 38, 38, 36, 39, 43, 38, 44, 38, 36, 37, 43, 44, 40, 37, 37, 39, 44, 43, 40, 38, 39, 37, 42, 38, 40, 38, 42, 39, 36, 40, 40, 36, 39, 43, 44, 35, 38, 37]分段: SUM: 593 Nodes: {4, 40}-{5, 44}-{6, 40}-{11, 39}-{12, 43}-{23, 38}-{24, 36}-{31, 37}-{33, 44}-{40, 40}-{42, 39}-{45, 38}-{55, 43}-{57, 35}-{59, 37}分段: SUM: 593 Nodes: {1, 36}-{3, 41}-{7, 40}-{13, 36}-{15, 39}-{18, 44}-{26, 43}-{27, 38}-{30, 36}-{35, 37}-{38, 44}-{44, 42}-{46, 40}-{47, 38}-{49, 39}分段: SUM: 593 Nodes: {9, 40}-{10, 36}-{14, 41}-{16, 36}-{19, 44}-{25, 39}-{29, 38}-{32, 43}-{36, 37}-{48, 42}-{50, 36}-{51, 40}-{54, 39}-{56, 44}-{58, 38}分段: SUM: 594 Nodes: {0, 44}-{2, 43}-{8, 37}-{17, 36}-{20, 38}-{21, 41}-{22, 38}-{28, 44}-{34, 40}-{37, 39}-{39, 43}-{41, 38}-{43, 37}-{52, 40}-{53, 36}
下面Nodes中{原始地位,原始数值}
欢送关注公众号,独特交换,共同进步