【状态压缩】【动态规划】【C++算法】1125.最小的必要团队

简介: 【状态压缩】【动态规划】【C++算法】1125.最小的必要团队

作者推荐

【广度优先搜索】【网格】【割点】【 推荐】1263. 推箱子

本文涉及知识点

动态规划汇总

状态压缩

LeetCode1125. 最小的必要团队

作为项目经理,你规划了一份需求的技能清单 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]

提示:

1 <= req_skills.length <= 16

1 <= req_skills[i].length <= 16

req_skills[i] 由小写英文字母组成

req_skills 中的所有字符串 互不相同

1 <= people.length <= 60

0 <= people[i].length <= 16

1 <= people[i][j].length <= 16

people[i][j] 由小写英文字母组成

people[i] 中的所有字符串 互不相同

people[i] 中的每个技能是 req_skills 中的技能

题目数据保证「必要团队」一定存在

动态规划

状态压缩

利用哈希map将字符串转成数字。方便状态压缩。mask &(1 <<i ) 表示第i个技能已经具备。

动态规划的状态

pre[mask] 从前i个人员中选择具备mask技能的最少人数。

dp[mask] 从前i+1个人员中选择具备mask技能的最少人数。

动态规划的转移方程

const int iNewMask = preMask | curMask;

人数必定+1。

动态规划的初始状态

pre[0]=0,其它1000,表示此种状态不存在。

动态规划的填表顺序

依次处理各人员。

动态规划的返回值

vResut[mask] 记录: 转移之前的状态和最后选择的人员。

代码

核心代码

class Solution {
public:
  vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
    unordered_map<string, int> mStrIndex;
    for (const auto& s : req_skills)
    {
      if (!mStrIndex.count(s))
      {
        mStrIndex[s] = mStrIndex.size();
      }
    }
    const int iMaskCount = 1 << mStrIndex.size();
    vector<int> pre(iMaskCount, 1000);
    vector<pair<int, int>> vResult(iMaskCount);
    pre[0] = 0;
    int cur = -1;
    for (const auto& v : people)
    {
      cur++;
      int curMask = 0;
      for (const string& s : v)
      {
        if (mStrIndex.count(s))
        {
          curMask |= (1 << mStrIndex[s]);
        }
      }
      auto dp = pre;//不选择当前人员
      for (int preMask = 0; preMask < iMaskCount; preMask++)
      {
        const int iNewMask = preMask | curMask;
        if (pre[preMask] + 1 < dp[iNewMask])
        {
          dp[iNewMask] = pre[preMask] + 1;
          vResult[iNewMask] = std::make_pair(preMask, cur);
        }
      }
      pre.swap(dp);
    }
    vector<int> vRet;
    for (int mask = iMaskCount - 1; mask > 0; )
    {
      vRet.emplace_back(vResult[mask].second);
      mask = vResult[mask].first;
    }
    return vRet;
  }
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{ 
  vector<string> req_skills;
  vector<vector<string>> people;
  {
    Solution sln;
    req_skills = { "java","nodejs","reactjs" }, people = { {"java"},{"nodejs"},{"nodejs","reactjs"} };
    auto res = sln.smallestSufficientTeam(req_skills, people);    
  //  Assert(vector<int>{0, 2}, res);
  }
  {
    Solution sln;
    req_skills = { "algorithms","math","java","reactjs","csharp","aws" }, people = { {"algorithms","math","java"},{"algorithms","math","reactjs"},{"java","csharp","aws"},{"reactjs","csharp"},{"csharp","math"},{"aws","java"} };
    auto res = sln.smallestSufficientTeam(req_skills, people);
    //Assert(vector<int>{1, 2}, res);
  }
}

2023年1月 第一版

class Solution {

public:

vector smallestSufficientTeam(vector& req_skills, vector<vector>& people) {

m_iBitNum = req_skills.size();

m_iMaskNum = (1 << m_iBitNum);

for (const auto& s : req_skills)

{

m_mSkillNameToIndex[s] = m_mSkillNameToIndex.size();

}

std::unordered_map<int,int> pre;

pre[0] = 0;

std::unordered_map<int, vector> preIndex(1);

for (int i = 0; i < people.size();i++ )

{

const auto& v = people[i];

std::unordered_map<int, int> dp = pre;

std::unordered_map<int, vector> dpIndex = preIndex;

for (const auto& pr : pre)

{

int iNewMask = pr.first;

for (const auto& s : v)

{

iNewMask |= (1 << m_mSkillNameToIndex[s]);

}

const int iNewNum = pr.second + 1;

if ((!dp.count(iNewMask)) || (iNewNum < dp[iNewMask]))

{

dp[iNewMask] = iNewNum;

dpIndex[iNewMask] = preIndex[pr.first];

dpIndex[iNewMask].push_back(i);

}

}

pre.swap(dp);

preIndex.swap(dpIndex);

}

return preIndex[m_iMaskNum - 1];

}

int m_iMaskNum;

int m_iBitNum;

std::unordered_map<string, int> m_mSkillNameToIndex;

};

2023年1月第二版

class Solution {

public:

vector smallestSufficientTeam(vector& req_skills, vector<vector>& people) {

m_iBitNum = req_skills.size();

m_iMaskNum = (1 << m_iBitNum);

for (const auto& s : req_skills)

{

m_mSkillNameToIndex[s] = m_mSkillNameToIndex.size();

}

std::vector pre(m_iMaskNum,1000);

pre[0] = 0;

std::vector<vector> preIndex(m_iMaskNum);

for (int i = 0; i < people.size();i++ )

{

const auto& v = people[i];

std::vector dp = pre;

std::vector<vector> dpIndex = preIndex;

for (int iMask = 0; iMask < m_iMaskNum; iMask++ )

{

int iNewMask = iMask;

for (const auto& s : v)

{

iNewMask |= (1 << m_mSkillNameToIndex[s]);

}

const int iNewNum = pre[iMask] + 1;

if (iNewNum < dp[iNewMask])

{

dp[iNewMask] = iNewNum;

dpIndex[iNewMask] = preIndex[iMask];

dpIndex[iNewMask].push_back(i);

}

}

pre.swap(dp);

preIndex.swap(dpIndex);

}

return preIndex[m_iMaskNum - 1];

}

int m_iMaskNum;

int m_iBitNum;

std::unordered_map<string, int> m_mSkillNameToIndex;

};

2023年1月 第3版

class Solution {

public:

vector smallestSufficientTeam(vector& req_skills, vector<vector>& people) {

m_iBitNum = req_skills.size();

m_iMaskNum = (1 << m_iBitNum);

for (const auto& s : req_skills)

{

m_mSkillNameToIndex[s] = m_mSkillNameToIndex.size();

}

vector vPeoMask;

for (const auto& v : people)

{

int iMask = 0;

for (const auto& s : v)

{

iMask |= (1 << m_mSkillNameToIndex[s]);

}

vPeoMask.push_back(iMask);

}

std::vector pre(m_iMaskNum,1000);

pre[0] = 0;

std::vector preIndex(m_iMaskNum);

for (int i = 0; i < people.size();i++ )

{

const auto& v = people[i];

std::vector dp = pre;

std::vector dpIndex = preIndex;

for (int iMask = 0; iMask < m_iMaskNum; iMask++ )

{

if (1000 == pre[iMask])

{

continue;

}

const int iNewMask = iMask | vPeoMask[i];

if (iNewMask == iMask)

{

continue;

}

const int iNewNum = pre[iMask] + 1;

if (iNewNum < dp[iNewMask])

{

dp[iNewMask] = iNewNum;

auto llNewPeoMask = (1LL) << i;

dpIndex[iNewMask] = preIndex[iMask] | llNewPeoMask;

}

}

pre.swap(dp);

preIndex.swap(dpIndex);

}

vector ret;

for (int i = 0; i < people.size(); i++)

{

const auto llNewPeoMask = (1LL) << i;

if (llNewPeoMask & preIndex[m_iMaskNum - 1])

{

ret.push_back(i);

}

}

return ret;

}

int m_iMaskNum;

int m_iBitNum;

std::unordered_map<string, int> m_mSkillNameToIndex;

};


相关文章
|
5天前
|
机器学习/深度学习 安全 算法
【图论】【割点】【C++算法】928. 尽量减少恶意软件的传播 II
【图论】【割点】【C++算法】928. 尽量减少恶意软件的传播 II
|
5天前
|
机器学习/深度学习 人工智能 算法
【图像版权】论文阅读:CRMW 图像隐写术+压缩算法
【图像版权】论文阅读:CRMW 图像隐写术+压缩算法
10 0
|
5天前
|
存储 缓存 算法
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
|
5天前
|
存储 算法 程序员
C++从入门到精通:2.2.1标准库与STL容器算法深度解析
C++从入门到精通:2.2.1标准库与STL容器算法深度解析
|
5天前
|
人工智能 算法 BI
【图论】【 割边】【C++算法】1192. 查找集群内的关键连接
【图论】【 割边】【C++算法】1192. 查找集群内的关键连接
|
5天前
|
算法 测试技术 C#
【模拟】【C++算法】2826. 将三个组排序
【模拟】【C++算法】2826. 将三个组排序
|
5天前
|
算法 数据安全/隐私保护 计算机视觉
基于二维CS-SCHT变换和LABS方法的水印嵌入和提取算法matlab仿真
该内容包括一个算法的运行展示和详细步骤,使用了MATLAB2022a。算法涉及水印嵌入和提取,利用LAB色彩空间可能用于隐藏水印。水印通过二维CS-SCHT变换、低频系数处理和特定解码策略来提取。代码段展示了水印置乱、图像处理(如噪声、旋转、剪切等攻击)以及水印的逆置乱和提取过程。最后,计算并保存了比特率,用于评估水印的稳健性。
|
2天前
|
算法
m基于BP译码算法的LDPC编译码matlab误码率仿真,对比不同的码长
MATLAB 2022a仿真实现了LDPC码的性能分析,展示了不同码长对纠错能力的影响。短码长LDPC码收敛快但纠错能力有限,长码长则提供更强纠错能力但易陷入局部最优。核心代码通过循环进行误码率仿真,根据EsN0计算误比特率,并保存不同码长(12-768)的结果数据。
20 9
m基于BP译码算法的LDPC编译码matlab误码率仿真,对比不同的码长
|
3天前
|
算法
MATLAB|【免费】融合正余弦和柯西变异的麻雀优化算法SCSSA-CNN-BiLSTM双向长短期记忆网络预测模型
这段内容介绍了一个使用改进的麻雀搜索算法优化CNN-BiLSTM模型进行多输入单输出预测的程序。程序通过融合正余弦和柯西变异提升算法性能,主要优化学习率、正则化参数及BiLSTM的隐层神经元数量。它利用一段简单的风速数据进行演示,对比了改进算法与粒子群、灰狼算法的优化效果。代码包括数据导入、预处理和模型构建部分,并展示了优化前后的效果。建议使用高版本MATLAB运行。
|
5天前
|
算法 计算机视觉
基于高斯混合模型的视频背景提取和人员跟踪算法matlab仿真
该内容是关于使用MATLAB2013B实现基于高斯混合模型(GMM)的视频背景提取和人员跟踪算法。算法通过GMM建立背景模型,新帧与模型比较,提取前景并进行人员跟踪。文章附有程序代码示例,展示从读取视频到结果显示的流程。最后,结果保存在Result.mat文件中。