今天遇到了一个这样的问题
一个序列分成 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中{原始位置,原始数值}
欢迎关注公众号,共同交流,共同进步
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。