33333

java 复制代码
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

java 复制代码
public class LabSampleInoutVo {
    private String lotid;
    private Integer quantity;
    public String getLotid() { return lotid; }
    public Integer getQuantity() { return quantity; }
}

111

调用:

java 复制代码
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 的版本。

相关推荐
java_prinln1 小时前
11. 说说 Single-Agent 和 Multi-Agent 的设计方案?
python
垆边人似月.1 小时前
第三篇:函数、文件与异常处理 / Functions, Files & Exception Handling
开发语言·python
我的xiaodoujiao1 小时前
Django 权限系统项目实战详细图文教程 4--系统登录功能实现 2
后端·python·django
云和数据.ChenGuang1 小时前
项目打开后没有主类
开发语言·人工智能·langchain·1024程序员节
鬼手点金1 小时前
缓存作为架构约束
开发语言·前端·人工智能·python·microsoft·缓存·架构
SEO_juper1 小时前
Node.js 页面主题聚焦度检测:title 和正文各说各的,AI 也读不懂
开发语言·人工智能·node.js·seo·外贸独立站
软件技术NINI1 小时前
基于PHP和MySQL的快递代收点管理系统的设计与实现
开发语言·javascript·数据库·mysql·毕业设计·php·毕设
Dovis(誓平步青云)2 小时前
一个分区超温就全棚浇水,控制系统怎样按区下策略
开发语言·前端·javascript·人工智能·机器学习
恩喜玛生物2 小时前
AIDD系列02:ProteinMPNN序列设计,从已知骨架到可检验的肽候选
python·ai