📌  相关文章
📜  从给定的依赖关系中查找是否有可能完成所有任务

📅  最后修改于: 2022-05-13 01:57:05.424000             🧑  作者: Mango

从给定的依赖关系中查找是否有可能完成所有任务

您必须选择总共 n 个任务,标记为从 0 到 n-1。有些任务可能有先决条件,例如要选择任务 0,您必须先选择任务 1,它表示为一对:[0, 1]
给定任务总数和先决条件对列表,您是否有可能完成所有任务?
例子:

问:谷歌、推特、亚马逊和更多公司。

解:我们可以把这个问题看成一个图(与拓扑排序有关)问题。所有任务都是图的节点,如果任务 u 是任务 v 的先决条件,我们将添加从节点 u 到节点 v 的有向边。现在,这个问题相当于在先决条件表示的图中检测一个循环。如果图中存在循环,则不可能完成所有任务(因为在这种情况下,任务没有任何拓扑顺序)。 BFS 和 DFS 都可以用来解决它。
由于pair对于图算法的实现不方便,我们先将其转化为图。如果任务 u 是任务 v 的先决条件,我们将添加一条从节点 u 到节点 v 的有向边。
先决条件:检测有向图中的循环
使用DFS对于 DFS,它将首先访问一个节点,然后访问它的一个邻居,然后再访问这个邻居的一个邻居……以此类推。如果遇到当前DFS访问过程中访问过的节点,则检测到一个循环,返回false。否则它将从另一个未访问的节点开始并重复此过程,直到所有节点都已访问。注意要做好两条记录:一是记录所有访问过的节点,二是记录当前DFS访问中访问过的节点。
代码如下。我们使用一个向量visited 来记录所有被访问的节点,另一个向量onpath 来记录当前DFS 访问的被访问节点。当前访问完成后,我们将起始节点的 onpath 值重置为 false。

CPP
// CPP program to check whether we can finish all
// tasks or not from given dependencies.
#include 
using namespace std;
 
// Returns adjacency list representation from a list
// of pairs.
vector > make_graph(int numTasks,
            vector >& prerequisites)
{
    vector > graph(numTasks);
    for (auto pre : prerequisites)
        graph[pre.second].insert(pre.first);
    return graph;
}
 
// A DFS based function to check if there is a cycle
// in the directed graph.
bool dfs_cycle(vector >& graph, int node,
               vector& onpath, vector& visited)
{
    if (visited[node])
        return false;
    onpath[node] = visited[node] = true;
    for (int neigh : graph[node])
        if (onpath[neigh] || dfs_cycle(graph, neigh, onpath, visited))
            return true;
    return onpath[node] = false;
}
 
// Main function to check whether possible to finish all tasks or not
bool canFinish(int numTasks, vector >& prerequisites)
{
    vector > graph = make_graph(numTasks, prerequisites);
    vector onpath(numTasks, false), visited(numTasks, false);
    for (int i = 0; i < numTasks; i++)
        if (!visited[i] && dfs_cycle(graph, i, onpath, visited))
            return false;
    return true;
}
 
int main()
{
    int numTasks = 4;
 
    vector > prerequisites;
 
    // for prerequisites: [[1, 0], [2, 1], [3, 2]]
 
    prerequisites.push_back(make_pair(1, 0));
    prerequisites.push_back(make_pair(2, 1));
    prerequisites.push_back(make_pair(3, 2));
    if (canFinish(numTasks, prerequisites)) {
        cout << "Possible to finish all tasks";
    }
    else {
        cout << "Impossible to finish all tasks";
    }
 
    return 0;
}


Java
// Java program to check whether we can finish all
// tasks or not from given dependencies.
import java.util.*;
 
public class GFG{
     
    // class to store dependencies as a pair
    static class pair{
        int first, second;
         
        pair(int first, int second){
            this.first = first;
            this.second = second;
        }
    }
     
    // Returns adjacency list representation from a list
    // of pairs.
    static ArrayList> make_graph(int numTasks,
                Vector prerequisites)
    {
        ArrayList> graph = new ArrayList>(numTasks);
 
        for(int i=0; i());
        }
 
        for (pair pre : prerequisites)
            graph.get(pre.second).add(pre.first);
 
        return graph;
    }
     
    // A DFS based function to check if there is a cycle
    // in the directed graph.
    static boolean dfs_cycle(ArrayList> graph, int node,
                boolean onpath[], boolean visited[])
    {
        if (visited[node])
            return false;
        onpath[node] = visited[node] = true;
 
        for (int neigh : graph.get(node))
            if (onpath[neigh] || dfs_cycle(graph, neigh, onpath, visited))
                return true;
 
        return onpath[node] = false;
    }
     
    // Main function to check whether possible to finish all tasks or not
    static boolean canFinish(int numTasks, Vector prerequisites)
    {
        ArrayList> graph = make_graph(numTasks, prerequisites);
         
        boolean onpath[] = new boolean[numTasks];
        boolean visited[] = new boolean[numTasks];
 
        for (int i = 0; i < numTasks; i++)
            if (!visited[i] && dfs_cycle(graph, i, onpath, visited))
                return false;
 
        return true;
    }
     
    public static void main(String args[])
    {
        int numTasks = 4;
     
        Vector prerequisites = new Vector();;
     
        // for prerequisites: [[1, 0], [2, 1], [3, 2]]
     
        prerequisites.add(new pair(1, 0));
        prerequisites.add(new pair(2, 1));
        prerequisites.add(new pair(3, 2));
         
        if (canFinish(numTasks, prerequisites)) {
            System.out.println("Possible to finish all tasks");
        }
        else {
            System.out.println("Impossible to finish all tasks");
        }
    }
}
 
// This code is contributed by adityapande88.


CPP
// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
#include 
using namespace std;
 
// Returns adjacency list representation from a list
// of pairs.
vector > make_graph(int numTasks,
            vector >& prerequisites)
{
    vector > graph(numTasks);
    for (auto pre : prerequisites)
        graph[pre.second].insert(pre.first);
    return graph;
}
 
// Finds in-degree of every vertex
vector compute_indegree(vector >& graph)
{
    vector degrees(graph.size(), 0);
    for (auto neighbors : graph)
        for (int neigh : neighbors)
            degrees[neigh]++;
    return degrees;
}
 
// Main function to check whether possible to finish all tasks or not
bool canFinish(int numTasks, vector >& prerequisites)
{
    vector > graph = make_graph(numTasks, prerequisites);
    vector degrees = compute_indegree(graph);
    for (int i = 0; i < numTasks; i++) {
        int j = 0;
        for (; j < numTasks; j++)
            if (!degrees[j])
                break;
        if (j == numTasks)
            return false;
        degrees[j] = -1;
        for (int neigh : graph[j])
            degrees[neigh]--;
    }
    return true;
}
 
int main()
{
    int numTasks = 4;
    vector > prerequisites;
    prerequisites.push_back(make_pair(1, 0));
    prerequisites.push_back(make_pair(2, 1));
    prerequisites.push_back(make_pair(3, 2));
    if (canFinish(numTasks, prerequisites)) {
        cout << "Possible to finish all tasks";
    }
    else {
        cout << "Impossible to finish all tasks";
    }
 
    return 0;
}


Java
// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
import java.util.*;
 
public class GFG{
     
    // class to store dependencies as a pair
    static class pair{
        int first, second;
         
        pair(int first, int second){
            this.first = first;
            this.second = second;
        }
    }
     
    // Returns adjacency list representation from a list
    // of pairs.
    static ArrayList> make_graph(int numTasks,
                Vector prerequisites)
    {
        ArrayList> graph = new ArrayList>(numTasks);
 
        for(int i=0; i());
        }
 
        for (pair pre : prerequisites)
            graph.get(pre.second).add(pre.first);
 
        return graph;
    }
     
    // Finds in-degree of every vertex
    static int[] compute_indegree(ArrayList> graph)
    {
        int degrees[] = new int[graph.size()];
 
        for (ArrayList neighbors : graph)
            for (int neigh : neighbors)
                degrees[neigh]++;
 
        return degrees;
    }
     
    // Main function to check whether possible to finish all tasks or not
    static boolean canFinish(int numTasks, Vector prerequisites)
    {
        ArrayList> graph = make_graph(numTasks, prerequisites);
        int degrees[] = compute_indegree(graph);
 
        for (int i = 0; i < numTasks; i++) {
            int j = 0;
            for (; j < numTasks; j++)
                if (degrees[j] == 0)
                    break;
 
            if (j == numTasks)
                return false;
 
            degrees[j] = -1;
            for (int neigh : graph.get(j))
                degrees[neigh]--;
        }
 
        return true;
    }
     
    public static void main(String args[])
    {
        int numTasks = 4;
        Vector prerequisites = new Vector();
         
        prerequisites.add(new pair(1, 0));
        prerequisites.add(new pair(2, 1));
        prerequisites.add(new pair(3, 2));
         
        if (canFinish(numTasks, prerequisites)) {
            System.out.println("Possible to finish all tasks");
        }
        else {
            System.out.println("Impossible to finish all tasks");
        }
     
    }
}
 
// This code is contributed by adityapande88.


Java
/*
* A union-find based solution for stating whether
* the given set of tasks with respective prequisites
* can be finished completely or not.
*/
import java.io.*;
import java.util.*;
 
//Driver code
class GFG {
    public static void main(String args[]) throws IOException
    {
             
            int prerequisites[][] = new int[][]{ {1,0}, {2,1}, {3,2} };
         
            Solution ob = new Solution();
       
            if(ob.isPossible(4, prerequisites))
            {
                System.out.println("Yes");
            }
            else{
                System.out.println("No");
            }
    }
}
//Driver Code Ends
 
 
//Solution code
class Solution {
  //returns true/false stating whether tasks can be finished or not 
  public boolean isPossible(int N, int[][] prerequisites)
    {
        //make object of Union class for N tasks
        Union u = new Union(N);
         
    //traverse through pre-requisites array
        for(int i = 0; i < prerequisites.length; i++){
          //check whether given pre-requisite pair
          //already have a common pre-requisite(parent)
          if(u.findParent(prerequisites[i][0]) ==
            u.findParent(prerequisites[i][1])) {
                 
                //tasks cannot be completed because there was
                //a cyclic condition in the tasks
                return false;
            }
             
          //make parent-child relation between pre-requisite task
          //and the task dependent on it
            u.makeParent(prerequisites[i][0], prerequisites[i][1]);
        }
         
        //if there was no cycle found, tasks can be completed
        return true;
    }
     
}
class Union {
    //to store the parents of respective tasks
    int[] arr;
   
    //parameterised constructor for Union class
    public Union(int n){
       
        arr = new int[n];
        //Initially, everyone is their own child
        for(int i = 0; i < n; i++){
            arr[i] = i;    
        }
    }
     
    public void makeParent(int a, int b){
        //find parent of b and make it a's parent
        arr[a] = findParent(b);
    }
    public int findParent(int c){
        //when an independent task is found 
        if(c == arr)
        return c;
        //recursively find the parent of given task
        return findParent(arr);
    }
}


输出
Possible to finish all tasks

使用BFS
BFS可以利用拓扑排序的思想来解决它。如果可以进行拓扑排序,则意味着没有循环,可以完成所有任务。
BFS 使用每个节点的入度。我们将首先尝试找到一个度数为 0 的节点。如果我们不这样做,则图中一定有一个循环,我们返回 false。否则我们找到了一个。我们将其入度设置为 -1 以防止再次访问它并将其所有邻居的入度减少 1。此过程将重复 n(节点数)次。如果我们没有返回 false,我们将返回 true。

CPP

// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
#include 
using namespace std;
 
// Returns adjacency list representation from a list
// of pairs.
vector > make_graph(int numTasks,
            vector >& prerequisites)
{
    vector > graph(numTasks);
    for (auto pre : prerequisites)
        graph[pre.second].insert(pre.first);
    return graph;
}
 
// Finds in-degree of every vertex
vector compute_indegree(vector >& graph)
{
    vector degrees(graph.size(), 0);
    for (auto neighbors : graph)
        for (int neigh : neighbors)
            degrees[neigh]++;
    return degrees;
}
 
// Main function to check whether possible to finish all tasks or not
bool canFinish(int numTasks, vector >& prerequisites)
{
    vector > graph = make_graph(numTasks, prerequisites);
    vector degrees = compute_indegree(graph);
    for (int i = 0; i < numTasks; i++) {
        int j = 0;
        for (; j < numTasks; j++)
            if (!degrees[j])
                break;
        if (j == numTasks)
            return false;
        degrees[j] = -1;
        for (int neigh : graph[j])
            degrees[neigh]--;
    }
    return true;
}
 
int main()
{
    int numTasks = 4;
    vector > prerequisites;
    prerequisites.push_back(make_pair(1, 0));
    prerequisites.push_back(make_pair(2, 1));
    prerequisites.push_back(make_pair(3, 2));
    if (canFinish(numTasks, prerequisites)) {
        cout << "Possible to finish all tasks";
    }
    else {
        cout << "Impossible to finish all tasks";
    }
 
    return 0;
}

Java

// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
import java.util.*;
 
public class GFG{
     
    // class to store dependencies as a pair
    static class pair{
        int first, second;
         
        pair(int first, int second){
            this.first = first;
            this.second = second;
        }
    }
     
    // Returns adjacency list representation from a list
    // of pairs.
    static ArrayList> make_graph(int numTasks,
                Vector prerequisites)
    {
        ArrayList> graph = new ArrayList>(numTasks);
 
        for(int i=0; i());
        }
 
        for (pair pre : prerequisites)
            graph.get(pre.second).add(pre.first);
 
        return graph;
    }
     
    // Finds in-degree of every vertex
    static int[] compute_indegree(ArrayList> graph)
    {
        int degrees[] = new int[graph.size()];
 
        for (ArrayList neighbors : graph)
            for (int neigh : neighbors)
                degrees[neigh]++;
 
        return degrees;
    }
     
    // Main function to check whether possible to finish all tasks or not
    static boolean canFinish(int numTasks, Vector prerequisites)
    {
        ArrayList> graph = make_graph(numTasks, prerequisites);
        int degrees[] = compute_indegree(graph);
 
        for (int i = 0; i < numTasks; i++) {
            int j = 0;
            for (; j < numTasks; j++)
                if (degrees[j] == 0)
                    break;
 
            if (j == numTasks)
                return false;
 
            degrees[j] = -1;
            for (int neigh : graph.get(j))
                degrees[neigh]--;
        }
 
        return true;
    }
     
    public static void main(String args[])
    {
        int numTasks = 4;
        Vector prerequisites = new Vector();
         
        prerequisites.add(new pair(1, 0));
        prerequisites.add(new pair(2, 1));
        prerequisites.add(new pair(3, 2));
         
        if (canFinish(numTasks, prerequisites)) {
            System.out.println("Possible to finish all tasks");
        }
        else {
            System.out.println("Impossible to finish all tasks");
        }
     
    }
}
 
// This code is contributed by adityapande88.
输出
Possible to finish all tasks

参考:
https://leetcode.com/problems/course-schedule/

使用联合查找

另一种方法是Union-find方法也可以用来解决这个问题。我们得到的每一对都可以被认为是父子关系。

一旦我们找到一对应该处于父子关系的任务,我们就会检查它们是否已经有一个共同的父级,因此这意味着任务中存在一个永远不会结束的依赖循环,因此解决了这样的问题一组任务是不可能的。

Java

/*
* A union-find based solution for stating whether
* the given set of tasks with respective prequisites
* can be finished completely or not.
*/
import java.io.*;
import java.util.*;
 
//Driver code
class GFG {
    public static void main(String args[]) throws IOException
    {
             
            int prerequisites[][] = new int[][]{ {1,0}, {2,1}, {3,2} };
         
            Solution ob = new Solution();
       
            if(ob.isPossible(4, prerequisites))
            {
                System.out.println("Yes");
            }
            else{
                System.out.println("No");
            }
    }
}
//Driver Code Ends
 
 
//Solution code
class Solution {
  //returns true/false stating whether tasks can be finished or not 
  public boolean isPossible(int N, int[][] prerequisites)
    {
        //make object of Union class for N tasks
        Union u = new Union(N);
         
    //traverse through pre-requisites array
        for(int i = 0; i < prerequisites.length; i++){
          //check whether given pre-requisite pair
          //already have a common pre-requisite(parent)
          if(u.findParent(prerequisites[i][0]) ==
            u.findParent(prerequisites[i][1])) {
                 
                //tasks cannot be completed because there was
                //a cyclic condition in the tasks
                return false;
            }
             
          //make parent-child relation between pre-requisite task
          //and the task dependent on it
            u.makeParent(prerequisites[i][0], prerequisites[i][1]);
        }
         
        //if there was no cycle found, tasks can be completed
        return true;
    }
     
}
class Union {
    //to store the parents of respective tasks
    int[] arr;
   
    //parameterised constructor for Union class
    public Union(int n){
       
        arr = new int[n];
        //Initially, everyone is their own child
        for(int i = 0; i < n; i++){
            arr[i] = i;    
        }
    }
     
    public void makeParent(int a, int b){
        //find parent of b and make it a's parent
        arr[a] = findParent(b);
    }
    public int findParent(int c){
        //when an independent task is found 
        if(c == arr)
        return c;
        //recursively find the parent of given task
        return findParent(arr);
    }
}
输出
Yes