You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
4.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2023_05_2_week;
import java.util.Arrays;
import java.util.List;
// 作为项目经理,你规划了一份需求的技能清单 req_skills
// 并打算从备选人员名单 people 中选出些人组成一个「必要团队」
// 编号为 i 的备选人员 people[i] 含有一份该备选人员掌握的技能列表)。
// 所谓「必要团队」,就是在这个团队中,
// 对于所需求的技能列表 req_skills 中列出的每项技能,
// 团队中至少有一名成员已经掌握。可以用每个人的编号来表示团队中的成员:
// 例如,团队 team = [0, 1, 3] 表示掌握技能分别为
// people[0]people[1],和 people[3] 的备选人员。
// 请你返回 任一 规模最小的必要团队,团队成员用人员编号表示。
// 你可以按 任意顺序 返回答案,题目数据保证答案存在。
// 测试链接 : https://leetcode.cn/problems/smallest-sufficient-team/
public class Code03_SmallestSufficientTeam {
// 需要的技能有 : "ABC" 、 "FG" 、 "ET"
// 人 "ABC" "ET" "BB"
// "ABC" 、 "FG" 、 "ET" -> "ABC" "ET" "FG"
// 0 1 2
// x : "ABC" "ET" "BB" : 2 1 0
// 0 1 1
public static int[] smallestSufficientTeam(String[] skills, List<List<String>> people) {
Arrays.sort(skills);
int n = skills.length;
int m = people.size();
int[] statuses = new int[m];
for (int i = 0; i < m; i++) {
int skillStatus = 0; // 00000000000000
List<String> skill = people.get(i);
skill.sort((a, b) -> a.compareTo(b));
int p1 = 0;
int p2 = 0;
while (p1 < n && p2 < skill.size()) {
int compare = skills[p1].compareTo(skill.get(p2));
if (compare < 0) {
p1++;
} else if (compare > 0) {
p2++;
} else {
skillStatus |= 1 << p1;
p1++;
p2++;
}
}
statuses[i] = skillStatus;
}
// 上面的过程,其实是把技能变成状态信息
// 比如:
// skills = { f,a,e,c}
// 排个序为 : a c e f
// 认为a是0技能
// 认为c是1技能
// 认为e是2技能
// 认为f是3技能
// 然后就可以把每个人的技能变成状态信息了
// 比如A会的技能 : e f t x c
// feca
// 认为A会的技能状态为 : 1110
// 解释一下 :
// 第0位是0说明A不会a技能
// 第1位是1说明A会c技能
// 第2位是1说明A会e技能
// 第3位是1说明A会f技能
// 至于t、x技能忽略因为没用
// 上面的过程,就是把技能编号
// 然后把每个人的技能变成位信息
// 比如skills一共8个技能
// 那么就是为了这个状态 : 00..0011111111
// 也就是什么时候凑齐8个1什么时候就可以结束了
int[][] dp = new int[m][1 << n];
for (int i = 0; i < m; i++) {
Arrays.fill(dp[i], -1);
}
// process函数去跑动态规划
// 目的是算出至少几人
// 以及填好动态规划表
int size = process(statuses, n, 0, 0, dp);
int[] ans = new int[size];
int ansi = 0;
int i = 0;
int status = 0;
// 大厂刷题班章节11
// 看一下,是根据动态规划表生成答案的路径
// 这就是为什么需要动态规划表,因为答案的路径可以用动态规划表生成
// 一定要看一下这个课
while (status != (1 << n) - 1) {
// 3人 3人
if (i + 1 == m || dp[i][status] != dp[i + 1][status]) {
ans[ansi++] = i;
status |= statuses[i];
}
i++;
}
return ans;
}
// 第i个人的技能列表是位信息就是people[i]people数组是固定参数
// 一定要凑齐n个技能n是固定参数
// 当前来到第i个人了可以要这个人也可以不要这个人i是可变信息
// 之前的技能哪个技能凑到了哪个技能还没凑到就是status你的目的是把它凑齐也就是凑齐n个1stauts是可变参数
// 返回值的含义 :
// 当前在people[i....]范围上选择人之前凑齐的技能状态是status请问所有技能都凑齐还需要至少几个人
// dp就是动态规划表记录返回值的
public static int process(int[] people, int n, int i, int status, int[][] dp) {
// n = 8
// 0000...0000 1111 1111
if (status == (1 << n) - 1) {
// 技能已经凑齐了
// 还需要0个人
return 0;
}
// 还没凑齐!
if (i == people.length) {
// 技能还没凑齐
// 但是人已经没了
// 怎么都凑不齐了
return Integer.MAX_VALUE;
}
if (dp[i][status] != -1) {
// 缓存命中,直接返回
return dp[i][status];
}
// 不要第i个人后续要几个人能凑齐
int p1 = process(people, n, i + 1, status, dp);
// 要第i个人后续要几个人能凑齐
int p2 = Integer.MAX_VALUE;
int next2 = process(people, n, i + 1, status | people[i], dp);
if (next2 != Integer.MAX_VALUE) {
p2 = 1 + next2;
}
// 选择人数最少的方案
int ans = Math.min(p1, p2);
dp[i][status] = ans;
return ans;
}
}