LeetCode 1125. 最小的必要团队 (可重覆盖,舞蹈链)

1125. 最小的必要团队

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

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

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

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

 

示例 1:

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

输入:req_skills = ["algorithms","math","java","reactjs","csharp","aws"], people = [["algorithms","math","java"],["algorithms","math","reactjs"],["java","csharp","aws"],["reactjs","csharp"],["csharp","math"],["aws","java"]]
输出:[1,2]

 

 

题解:很显然的可重覆盖问题了,舞蹈链直接解即可,关于舞蹈链,详见:

当然,本题就面试而言,还是先答一下DP+二进制记录状态。

struct DLX{
    static const int maxn = 111;
    static const int maxm = 111;
    static const int maxnode = maxn*maxm;
	int n,m,size;
	int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
	int H[maxn],S[maxm];
	int ansd,ans1[111111], ans[111111];
	void init(int _n,int _m)
	{
		n = _n,m = _m;
		for(int i = 0;i<=m;++i)
		{
			S[i] = 0;
			U[i] = D[i] = i;
			L[i] = i-1;
			R[i] = i+1; 
		}
		R[m] = 0,L[0] = m;
		size = m;
		for(int i = 1;i<=n;++i)H[i] = -1;

	}

	void link(int r,int c)
	{
		++S[ Col[++size] = c ];
		Row[size] = r;

		D[size] = D[c];
		U[ D[c] ] = size;
		U[size] = c;
		D[c] = size;

		if(H[r] < 0)H[r] = L[size] = R[size] = size;
		else
		{
			R[size] = R[H[r]];
			L[R[H[r]]] = size;
			L[size] = H[r];
			R[H[r]] = size;
		}
	}

	void remove(int c)
	{
		for(int i = D[c];  i != c;i = D[i])
			L[ R[i] ] = L[i],R[ L[i] ] = R[i];
	}

	void resume(int c)
	{
		for(int i = U[c];  i != c;i = U[i])
			L[R[i]] = R[ L[i] ] = i;
	}

	bool v[maxm];

	int f()
	{
		int res = 0;
		for(int c = R[0];c;c = R[c])v[c] = true;

		for(int c = R[0];c;c=R[c])
			if(v[c])
			{
				res++;
				v[c] = false;
				for(int i = D[c];i!=c;i = D[i])
					for(int j = R[i];j!=i;j = R[j])
						v[ Col[j] ] = false;
 			}

 		return res;
	}

	void dance(int d)
	{
        
		if(d+f() >= ansd)return;
		if(R[0] == 0)
		{
			if(d < ansd)
            {
                ansd = d;
                for(int i = 0;i<d;++i)
                    ans[i] = ans1[i];
            }
			return;
		}

		int c = R[0];
		for(int i = R[0];i;i = R[i])
			if(S[i] < S[c])
				c = i;

		for(int i = D[c];i != c;i = D[i])
		{
            ans1[d] = Row[i];
			remove(i);
			for(int j = R[i];j != i;j = R[j])remove(j);
			dance(d+1);
			for(int j = L[i];j != i; j = L[j])resume(j);
			resume(i);
		}	
	}	
}g;

class Solution {
public:
    vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
        
        unordered_map<string,int> mp;
        for(int i = 0;i < (int)req_skills.size();++i)
            mp[req_skills[i]] = i+1;
        
        int n = people.size(),m = req_skills.size();
        g.init(n,m);
        
        for(int x = 0;x<(int)people.size();++x)
        {
            for(int i = 0;i<(int)people[x].size();++i)
            {
                int y = mp[people[x][i]];
                g.link(x+1,y);
            }
        }
        g.ansd = 11111111;
        g.dance(0);
            
        vector<int>ans;
        
        for(int i = 0;i<g.ansd;++i)
        {
            ans.push_back(g.ans[i]-1);
        }
        return ans;
 
            
    }
};

 

 

 

DP+二进制记录状态:

 

class Solution {
public:
    vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
        int n = req_skills.size();
        int full = 1<<n;
        unordered_map<string,int> skills;
        vector<int> dp(full,-1);
        unordered_map<int,vector<int>> team;
        
        /*initial*/
        for(int i = 0;i < n; ++i){
            skills[req_skills[i]] = i;
        }
        
        dp[0] = 0;
        team[0] = vector<int>();
        
        for(int i = 0;i < people.size(); ++i){
            int idx = 0;
            for(auto s : people[i]){
                if(skills.count(s)){
                    idx = idx|(1<<skills[s]);
                }
            }
            for(int j = 0;j < full; ++j){
                if(dp[j] >= 0){
                    int x = j|idx;
                    if(dp[x] == -1 || dp[x] > dp[j] + 1){
                        dp[x] = dp[j] + 1;
                        team[x] = team[j];
                        team[x].push_back(i);
                    }
                }
            }
        }
        
        return team[full-1];
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值