【状压 dp】A003_LC_最小的必要团队(Map)

一、Problem

作为项目经理,你规划了一份需求的技能清单 req_skills,并打算从备选人员名单 people 中选出些人组成一个「必要团队」( 编号为 i 的备选人员 people[i] 含有一份该备选人员掌握的技能列表)。

所谓「必要团队」,就是在这个团队中,对于所需求的技能列表 req_skills 中列出的每项技能,团队中至少有一名成员已经掌握。

我们可以用每个人的编号来表示团队中的成员:例如,团队 team = [0, 1, 3] 表示掌握技能分别为 people[0],people[1],和 people[3] 的备选人员。

请你返回 任一 规模最小的必要团队,团队成员用人员编号表示。你可以按任意顺序返回答案,本题保证答案存在。

输入:req_skills = ["java","nodejs","reactjs"], people = [["java"],["nodejs"],["nodejs","reactjs"]]
输出:[0,2]

提示:

1 <= req_skills.length <= 16
1 <= people.length <= 60
1 <= people[i].length, req_skills[i].length, people[i][j].length <= 16
req_skills 和 people[i] 中的元素分别各不相同
req_skills[i][j], people[i][j][k] 都由小写英文字母组成
本题保证「必要团队」一定存在

二、Solution

方法一:dp

思路

需满足的技能数量很小,暗示我们可以将团队的技能状态用一个整形变量来记录;首先在状态转移之前,我们肯定需要得到某一个人的掌握的技能,然后将该人的技能状态跟已经存在的团队的技能状态 j 相融合,设融合后的状态为 y:

  • 如果状态 y 之前不存在,那么团队技能状态 y 所需的人数直接加上该人
  • 如果状态 y 之前存在过,那么团队技能状态 y 所需的人数应该与状态为 jj 时加上 1 人对比,看谁所需人数少

算法

  • 定义状态
    • f[i]f[i] 表示团队中技能满足状态为 ii 时,所需要的最少人数
  • 思考初始化:
    • f[0]=0f[0] = 0 表示没有任何一项技能满足时的团队需要 0 个人
    • f[1...tot]=1f[1...tot] = -1
  • 思考状态转移方程
    • 如果 f[y]=1f[y] = -1 或者 f[y]>f[j]+1f[y] > f[j] + 1,则 f[y]=f[j]+1f[y] = f[j] + 1
  • 思考输出:输出满足所有能后的最少人数团队成员编号。

由于字符串难以做成位操作,所以要将字符串和技能编号映射起来。

class Solution {
    public int[] smallestSufficientTeam(String[] rss, List<List<String>> ps) {
    	int n = rss.length, m = ps.size(), tot = (1 << n) - 1, f[] = new int[tot+1];
    	Arrays.fill(f, -1);
    	f[0] = 0;
    	Map<String, Integer> mp = new HashMap<>();
    	Map<Integer, List<Integer>> team = new HashMap<>();
    	for (int i = 0; i < n; i++)     mp.put(rss[i], i);
        for (int i = 0; i <= tot; i++)  team.put(i, new LinkedList<>());

    	for (int i = 0; i < m; i++) {
    		int x = 0;
    		for (String s : ps.get(i))
    			x |= 1 << mp.get(s);
    		for (int j = 0; j <= tot; j++) if (f[j] != -1) {
    			int y = j | x;
    			if (f[y] == -1 || f[y] > f[j] + 1) {
    				f[y] = f[j] + 1;
    				team.get(y).clear();
                    team.get(y).addAll(team.get(j));
    				team.get(y).add(i);
    			}
    		}
    	}
    	int sz = team.get(tot).size(), i = 0, ans[] = new int[sz];
    	for (int pID : team.get(tot)) 
    		ans[i++] = pID;
    	return ans;
    }
}

复杂度分析

  • 时间复杂度:O(m×(1<<n))O(m × (1 << n))
  • 空间复杂度:O(1<<n)O(1<<n)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章