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

1 year ago
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;
}
}