略略略咯咯头像

33333

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class LocationAllocator {

    private static final Pattern LOC_PATTERN = Pattern.compile("^(L\\d+)-(\\d+)$");
    private static final int MIN_SLOT = 1;
    private static final int MAX_SLOT = 25;
    private static final int MAX_ROWS_PER_LOT = 3;

    /** lotid -> 分到的库位列表 */
    public Map<String, List<String>> allocate(List<String> locations, List<LabSampleInoutVo> list) {
        // 1. 解析并按行分组,行内按槽位排序
        Map<String, List<Slot>> rowMap = groupAvailable(locations);

        // 行顺序:L1, L2, L3 ...(按行号数字,不是字符串)
        List<String> rowKeys = new ArrayList<>(rowMap.keySet());
        rowKeys.sort(Comparator.comparingInt(this::rowNumber));

        // 2. 同一 lotid 数量合并,再按 quantity 升序
        List<LotNeed> needs = mergeAndSort(list);

        Map<String, List<String>> result = new LinkedHashMap<>();

        for (LotNeed need : needs) {
            List<String> chosenRows = findRows(rowKeys, rowMap, need.quantity);
            if (chosenRows == null) {
                throw new IllegalStateException(
                    "无法为 lotid=" + need.lotid + " 分配 " + need.quantity
                    + " 个位置(最多组合 " + MAX_ROWS_PER_LOT + " 行)");
            }
            List<String> assigned = takeSlots(rowMap, chosenRows, need.quantity);
            result.put(need.lotid, assigned);
        }
        return result;
    }

    private Map<String, List<Slot>> groupAvailable(List<String> locations) {
        Map<String, List<Slot>> map = new LinkedHashMap<>();
        if (locations == null) {
            return map;
        }
        for (String loc : locations) {
            if (loc == null) {
                continue;
            }
            Matcher m = LOC_PATTERN.matcher(loc.trim());
            if (!m.matches()) {
                continue;
            }
            String row = m.group(1); // L1
            int slot = Integer.parseInt(m.group(2));
            if (slot < MIN_SLOT || slot > MAX_SLOT) {
                continue;
            }
            map.computeIfAbsent(row, k -> new ArrayList<>()).add(new Slot(loc.trim(), slot));
        }
        for (List<Slot> slots : map.values()) {
            slots.sort(Comparator.comparingInt(s -> s.slot));
        }
        return map;
    }

    private List<LotNeed> mergeAndSort(List<LabSampleInoutVo> list) {
        Map<String, Integer> qtyByLot = new LinkedHashMap<>();
        for (LabSampleInoutVo vo : list) {
            if (vo == null || vo.getLotid() == null || vo.getQuantity() == null) {
                continue;
            }
            qtyByLot.merge(vo.getLotid(), vo.getQuantity(), Integer::sum);
        }
        return qtyByLot.entrySet().stream()
            .map(e -> new LotNeed(e.getKey(), e.getValue()))
            .sorted(Comparator.comparingInt((LotNeed n) -> n.quantity)
                .thenComparing(n -> n.lotid))
            .collect(Collectors.toList());
    }

    /** 先 1 行,再 2 行,再 3 行;同层按行号组合顺序,找到第一个剩余之和 >= quantity */
    private List<String> findRows(List<String> rowKeys, Map<String, List<Slot>> rowMap, int quantity) {
        int n = rowKeys.size();
        for (int k = 1; k <= MAX_ROWS_PER_LOT; k++) {
            List<Integer> idx = new ArrayList<>();
            List<String> found = new ArrayList<>();
            if (dfsCombine(rowKeys, rowMap, quantity, k, 0, idx, found)) {
                return found;
            }
        }
        return null;
    }

    private boolean dfsCombine(List<String> rowKeys, Map<String, List<Slot>> rowMap,
                               int quantity, int k, int start,
                               List<Integer> idx, List<String> out) {
        if (idx.size() == k) {
            int sum = 0;
            for (int i : idx) {
                sum += remaining(rowMap, rowKeys.get(i));
            }
            if (sum >= quantity) {
                out.clear();
                for (int i : idx) {
                    out.add(rowKeys.get(i));
                }
                return true;
            }
            return false;
        }
        for (int i = start; i < rowKeys.size(); i++) {
            if (remaining(rowMap, rowKeys.get(i)) <= 0) {
                continue;
            }
            idx.add(i);
            if (dfsCombine(rowKeys, rowMap, quantity, k, i + 1, idx, out)) {
                return true;
            }
            idx.remove(idx.size() - 1);
        }
        return false;
    }

    private int remaining(Map<String, List<Slot>> rowMap, String row) {
        List<Slot> slots = rowMap.get(row);
        return slots == null ? 0 : slots.size();
    }

    /** 按选中行顺序,每行从左到右(槽位号升序)取走 */
    private List<String> takeSlots(Map<String, List<Slot>> rowMap, List<String> rows, int quantity) {
        List<String> assigned = new ArrayList<>(quantity);
        int left = quantity;
        for (String row : rows) {
            List<Slot> slots = rowMap.get(row);
            if (slots == null || slots.isEmpty()) {
                continue;
            }
            int take = Math.min(left, slots.size());
            for (int i = 0; i < take; i++) {
                assigned.add(slots.get(i).code);
            }
            slots.subList(0, take).clear();
            left -= take;
            if (left == 0) {
                break;
            }
        }
        return assigned;
    }

    private int rowNumber(String row) {
        return Integer.parseInt(row.substring(1));
    }

    private static class Slot {
        final String code;
        final int slot;
        Slot(String code, int slot) {
            this.code = code;
            this.slot = slot;
        }
    }

    private static class LotNeed {
        final String lotid;
        final int quantity;
        LotNeed(String lotid, int quantity) {
            this.lotid = lotid;
            this.quantity = quantity;
        }
    }
}

1

public class LabSampleInoutVo {
    private String lotid;
    private Integer quantity;
    public String getLotid() { return lotid; }
    public Integer getQuantity() { return quantity; }
}

111

调用:

Map<String, List<String>> assigned = new LocationAllocator().allocate(locations, list);

2

规则理解

这是一个按行(L1、L2…)分组、优先少用行、最多拼 3 行的库位分配问题。当前仓库是 Godot 项目,没有现成 Java 类,下面按你描述的规则给出可直接用的实现。

规则理解

  1. 只认 L数字-1~25,例如 L1-1、L2-25;locations 里没有的就是已被占用。
  2. 同一 lotid 视为一种,按 quantity 要几个空位。
  3. 数量少的 lot 先分,避免大单把零散空位占完。
  4. 按行号分组:L1 剩几个、L2 剩几个。
  5. 对某个 quantity = Q:
    • 先找一行剩余 >= Q
    • 没有则两行相加:L1+L2 → L1+L3 → … → L2+L3 …
    • 再没有则三行:L1+L2+L3 …
    • 三行仍不够就报错
  6. 一行内按槽位号从小到大取(L1-1 … L1-25 中还空着的)。

你举例里「只要 1 个时 L1 满足」按第一行剩余 ≥ 需求理解;「L1 还剩唯一空位」是另一种数据下的特例。

对应你的例子

假设剩余:L1=5, L2=4, L3=3, L4=8

需求 Q怎么选

1

第一行剩余 ≥ 1 → 用 L1 里槽位最小的那个

8

单行:L4 正好 ≥ 8

9

单行都不够 → L1+L2=9 刚好

10

L1+L2=9 不够 → L1+L3=8 不够 → L1+L4=13 够

20

两行最大 L1+L4=13 不够 → 试三行,例如 L1+L2+L4=17 仍不够则继续,直到某三行之和 ≥ 20;都不行就报错

分完一个 lot 后,这些库位会从剩余里删掉,再处理下一个(数量更大的)lot。


需要你确认的两点

  1. 同一 lotid 多条记录:上面是合并 quantity。若每条要单独分配,把 mergeAndSort 改成不合并即可。
  2. 两行组合顺序:按 L1+L2, L1+L3, …(行号升序组合)。若必须固定「始终以当前最小行号为锚」也可以,当前实现就是这个。

如果你把 LabSampleInoutVo 真实字段名、以及分配结果要写回哪个字段(例如 location)发我,我可以改成贴合你们现有 Service 的版本。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/m0_62474094/article/details/169266985

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--