公司共有 n 个项目和 m 个小组,每个项目要不无人接手,要不就由 m 个小组之一负责。
group[i] 表示第 i 个项目所属的小组,如果这个项目目前无人接手,那么 group[i] 就等于 -1。(项目和小组都是从零开始编号的)小组可能存在没有接手任何项目的情况。
请你帮忙按要求安排这些项目的进度,并返回排序后的项目列表:
同一小组的项目,排序后在列表中彼此相邻。
项目之间存在一定的依赖关系,我们用一个列表 beforeItems 来表示,其中 beforeItems[i] 表示在进行第 i 个项目前(位于第 i 个项目左侧)应该完成的所有项目。
如果存在多个解决方案,只需要返回其中任意一个即可。如果没有合适的解决方案,就请返回一个 空列表 。
示例 1:
输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]]
输出:[6,3,4,1,5,2,0,7]
示例 2:
输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3],[],[4],[]]
输出:[]
解释:与示例 1 大致相同,但是在排序后的列表中,4 必须放在 6 的前面。
提示:
1 <= m <= n <= 3 * 104
group.length == beforeItems.length == n
-1 <= group[i] <= m - 1
0 <= beforeItems[i].length <= n - 1
0 <= beforeItems[i][j] <= n - 1
i != beforeItems[i][j]
beforeItems[i] 不含重复元素
基本思路:基于任务计划项目之间的明显的前后依赖关系,可以用拓扑排序解决问题。有两个核心点和3个注意点。
1.两个核心点:
- 怎样进行拓扑排序:使用BFS从入度为零的点开始,将邻居逐步加入队列。具体上是将相邻点入度减去一,若入度为零,则加入队列中,循环进行,直至队列为空。
- 怎么将同组的项目安排在一起:完成group2item的映射,然后根据组拓扑排序的顺序,将group转换成item。
2.三个注意点:
- group[i]==-1,仅仅表示的是未安排,不是代表所有为-1的是同一组,需要预处理,防止误判
- 组的邻接矩阵的获得,根据项目列表和beforeItems 获得组的依赖关系,如果当前项目于beforeitems[i]中的项目属于同一组,天然满足要求,直接pass
- 在拓扑排序中,要注意环的问题,判断依据 ans.size()==n
vector<int> topLogicalSort(vector<vector<int>> &adj,vector<int> &inDegree,int n){
//dfs;
queue<int> q;
vector<int> ans;
for(int i=0;i<inDegree.size();i++){
if(inDegree[i]==0)
q.push(i);
}
while(!q.empty()){
int top=q.front();
q.pop();
ans.push_back(top);
for(auto sub:adj[top]){
inDegree[sub]--;
if(inDegree[sub]==0){
q.push(sub);
}
}
}
if(ans.size()==n)
return ans;
return vector<int> ();
}
void print(vector<int> &aa){
for(auto a:aa){
cout<<a<<" ";
}
cout<<endl;
}
vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
//1.预处理,防止没有安排的项目组,被误认为同一个组
for(int i=0;i<group.size();i++){
if(group[i]==-1){
group[i]=m;
m++;
}
}
//2.构建邻接表和统计入度。
vector<vector<int>> itemAdj(n,vector<int>());
vector<vector<int>> groupAdj(m,vector<int>());
vector<int> itemIndegree(n,0);
vector<int> groupIndegree(m,0);
for(int i=0;i<n;i++){
for(auto item:beforeItems[i]){
itemAdj[item].push_back(i);
itemIndegree[i]++;
}
}
for(int i=0;i<n;i++){
for(auto item:beforeItems[i]){
if(group[item]==group[i]) //若是处于相同的组,那么肯定不会冲突
continue;
groupAdj[group[item]].push_back(group[i]);
groupIndegree[group[i]]++;
}
}
//3.得到组和项目的拓扑排序
vector<int> ans;
auto groupList=topLogicalSort(groupAdj,groupIndegree,m);
if(groupList.size()==0){
return ans;
}
auto itemList=topLogicalSort(itemAdj,itemIndegree,n);
if(itemList.size()==0){
return ans; //同时满足组和item的拓扑排序,那么必然有解
}
//4.用哈希表做组到项目的一对多的映射,直接将项目替换为项目,就是结果。
unordered_map<int,vector<int>> group2item;
for(auto item:itemList){
group2item[group[item]].push_back(item);
}
for(auto gid:groupList){
for(auto item:group2item[gid]){
ans.push_back(item);
}
}
return ans;
}