dev_1.0.1
luotaiqian 2026-07-16 15:35:14 +08:00
parent c187397c3e
commit b91c2d09a4
2 changed files with 162 additions and 42 deletions

View File

@ -21,7 +21,6 @@ import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static org.qinan.safetyeval.domain.exception.ErrorCode.PERSON_NOT_ENOUGH;
@ -385,13 +384,11 @@ public class ComplianceCheckUtil {
}
// 数据来源industryProfessionalPersonCos备案人员自带专业能力+ certCapabilityList证书专业能力
// 4. 贪心分配(一人一岗):按可选人数从少到多处理,优先满足最紧缺的专业能力
Map<String, Integer> assignedMap = assignPersonnelGreedy(personCapabilityMap, requiredMap);
boolean allMet = canFullyAssign(personCapabilityMap, requiredMap);
// 5. 判断是否所有专业能力都达标,并生成失败消息
boolean allMet = true;
IndustryEnum industryEnum = IndustryEnum.ofCode(standardsDOList.get(0).getIndustryCode());
String industryName = industryEnum != null ? industryEnum.getValue() : standardsDOList.get(0).getIndustryCode();
@ -417,53 +414,94 @@ public class ComplianceCheckUtil {
return allMet ? Pair.of(true, null) : Pair.of(false, failureMsg.toString());
}
/**
*
* <p>
* 使
*
* <p>
* "只需判断能否达标"
* 使
*
* @param personCapabilityMap code
* @param requiredMap code
* @return code
* @param personCapabilityMap ID code
* @param requiredMap code
* @return true
*/
private static Map<String, Integer> assignPersonnelGreedy(Map<Long, Set<String>> personCapabilityMap
, Map<String, Long> requiredMap) {
// 构建 专业能力code → 可用人员集合
Map<String, Set<Long>> capabilityPersonMap = new HashMap<>();
public static boolean canFullyAssign(
Map<Long, Set<String>> personCapabilityMap, Map<String, Long> requiredMap) {
// ---- 1. 构图 ----
// 节点编号分配
// 源点: 0
// 人员节点: 1 .. P
// 能力节点: P+1 .. P+C
// 汇点: P+C+1
List<Long> personIds = new ArrayList<>(personCapabilityMap.keySet());
List<String> capCodes = new ArrayList<>(requiredMap.keySet());
int P = personIds.size();
int C = capCodes.size();
int S = 0;
int T = P + C + 1;
int totalNodes = T + 1;
Dinic dinic = new Dinic(totalNodes);
// 建立人员ID到节点编号的映射
Map<Long, Integer> personNode = new HashMap<>();
for (int i = 0; i < P; i++) {
personNode.put(personIds.get(i), i + 1);
}
// 建立能力code到节点编号的映射
Map<String, Integer> capNode = new HashMap<>();
for (int i = 0; i < C; i++) {
capNode.put(capCodes.get(i), P + 1 + i);
}
long totalRequired = 0;
// 源点 → 人员 (容量1)
for (int i = 0; i < P; i++) {
dinic.addEdge(S, personNode.get(personIds.get(i)), 1);
}
// 人员 → 能力 (容量1)
for (Map.Entry<Long, Set<String>> entry : personCapabilityMap.entrySet()) {
Long pid = entry.getKey();
int u = personNode.get(pid);
for (String cap : entry.getValue()) {
if (requiredMap.containsKey(cap)) {
capabilityPersonMap.computeIfAbsent(cap, k -> new HashSet<>()).add(pid);
if (capNode.containsKey(cap)) { // 仅连有需求的能力
dinic.addEdge(u, capNode.get(cap), 1);
}
}
}
// 按可选人数从少到多排序,优先满足最紧缺的专业能力
List<String> capOrder = new ArrayList<>(requiredMap.keySet());
capOrder.sort(Comparator.comparingInt(c -> capabilityPersonMap.getOrDefault(c, Collections.emptySet()).size()));
Set<Long> usedPersonIds = new HashSet<>();
Map<String, Integer> assignedMap = new LinkedHashMap<>();
for (String capCode : capOrder) {
long required = requiredMap.get(capCode);
Set<Long> candidates = capabilityPersonMap.get(capCode);
int assigned = 0;
if (candidates != null) {
for (Long pid : candidates) {
if (assigned >= required) {
break;
}
if (usedPersonIds.add(pid)) {
assigned++;
}
}
}
assignedMap.put(capCode, assigned);
// 能力 → 汇点 (容量 = 需求人数)
for (Map.Entry<String, Long> entry : requiredMap.entrySet()) {
String cap = entry.getKey();
long req = entry.getValue();
totalRequired += req;
int v = capNode.get(cap);
dinic.addEdge(v, T, req);
}
return assignedMap;
// ---- 2. 计算最大流 ----
long maxFlow = dinic.maxFlow(S, T);
return maxFlow == totalRequired;
}
// 简单测试
public static void main(String[] args) {
Map<Long, Set<String>> personMap = new HashMap<>();
personMap.put(1L, new HashSet<>(Arrays.asList("A", "B")));
personMap.put(2L, new HashSet<>(Arrays.asList("A", "B")));
personMap.put(3L, new HashSet<>(Arrays.asList("A")));
personMap.put(4L, new HashSet<>(Arrays.asList("B")));
personMap.put(5L, new HashSet<>(Arrays.asList("A")));
Map<String, Long> required = new HashMap<>();
required.put("A", 2L);
required.put("B", 2L);
System.out.println(canFullyAssign(personMap, required)); // true
// 让需求不可满足A需要3人B需要2人
required.put("A", 3L);
System.out.println(canFullyAssign(personMap, required)); // false
}
}

View File

@ -0,0 +1,82 @@
package org.qinan.safetyeval.infrastructure.utils;
import java.util.*;
/**
* Dinic
*/
public class Dinic {
static class Edge {
int to, rev;
long cap;
Edge(int to, int rev, long cap) {
this.to = to;
this.rev = rev;
this.cap = cap;
}
}
List<Edge>[] graph;
int[] level, iter;
@SuppressWarnings("unchecked")
Dinic(int n) {
graph = new ArrayList[n];
for (int i = 0; i < n; i++) graph[i] = new ArrayList<>();
level = new int[n];
iter = new int[n];
}
void addEdge(int from, int to, long cap) {
graph[from].add(new Edge(to, graph[to].size(), cap));
graph[to].add(new Edge(from, graph[from].size() - 1, 0));
}
void bfs(int s) {
Arrays.fill(level, -1);
Queue<Integer> q = new LinkedList<>();
level[s] = 0;
q.offer(s);
while (!q.isEmpty()) {
int v = q.poll();
for (Edge e : graph[v]) {
if (e.cap > 0 && level[e.to] < 0) {
level[e.to] = level[v] + 1;
q.offer(e.to);
}
}
}
}
long dfs(int v, int t, long f) {
if (v == t) return f;
for (int i = iter[v]; i < graph[v].size(); i++) {
iter[v] = i;
Edge e = graph[v].get(i);
if (e.cap > 0 && level[v] < level[e.to]) {
long d = dfs(e.to, t, Math.min(f, e.cap));
if (d > 0) {
e.cap -= d;
graph[e.to].get(e.rev).cap += d;
return d;
}
}
}
return 0;
}
long maxFlow(int s, int t) {
long flow = 0;
while (true) {
bfs(s);
if (level[t] < 0) break;
Arrays.fill(iter, 0);
long f;
while ((f = dfs(s, t, Long.MAX_VALUE)) > 0) {
flow += f;
}
}
return flow;
}
}