这个问题叫Strip packing,维基百科上介绍了几种算法。下面的实现是FFDH。
import java.util.*;
public class StripPacking {
static class Rectangle {
int w;
int h;
public Rectangle(int w, int h)
{
this.w = w;
this.h = h;
}
}
static class Level {
public Level(int stripW)
{
this.stripW = stripW;
}
final int stripW;
int usedW = 0;
ArrayList<Rectangle> rects = new ArrayList<Rectangle>();
public boolean TryAdd(Rectangle r)
{
if(usedW + r.w <= stripW) {
rects.add(r);
usedW += r.w;
return true;
}
return false;
}
public String toString() {
var s = new StringBuilder("Level height ");
s.append(rects.get(0).h).append(", rectangles: ");
for(var r : rects) {
s.append("(w:").append(r.w).append(", h:").append(r.h).append(") ");
}
return s.toString();
}
}
public static void FFDH(int stripWidth, Rectangle[] rects)
{
// Sort by h from big to small
Arrays.sort(rects, (a, b) -> Integer.compare(b.h, a.h));
var levels = new ArrayList<Level>();
for(var r : rects) {
if(r.w > stripWidth) {
continue;
}
boolean added = false;
for(var level : levels) {
if(level.TryAdd(r)) {
added = true;
break;
}
}
if(!added) {
// add a new level
var level = new Level(stripWidth);
level.TryAdd(r);
levels.add(level);
}
}
int totalH = 0;
for(var level : levels) {
totalH += level.rects.get(0).h;
System.out.println(level);
}
System.out.println("total height used " + totalH);
}
public static void main(String[] args)
{
var rects = new Rectangle[] {
new Rectangle(6, 8),
new Rectangle(5, 7),
new Rectangle(4, 6),
new Rectangle(7, 5),
new Rectangle(3, 4),
new Rectangle(4, 3),
new Rectangle(6, 2),
};
FFDH(18, rects);
}
}
例子也是维基百科上的:
输出结果:
Level height 8, rectangles: (w:6, h:8) (w:5, h:7) (w:4, h:6) (w:3, h:4)
Level height 5, rectangles: (w:7, h:5) (w:4, h:3) (w:6, h:2)
total height used 13