📜  作业排序问题 |组 2(使用不相交组)

📅  最后修改于: 2021-10-25 11:24:28             🧑  作者: Mango

给定一组 n 个工作,其中每个工作 i 的截止日期 di >=1 且利润 pi>=0。一次只能安排一项作业。每项工作需要 1 个单位的时间来完成。当且仅当工作在截止日期前完成时,我们才能获得利润。任务是找到使利润最大化的工作子集。

例子:

Input: Four Jobs with following deadlines and profits
JobID Deadline Profit
   a      4      20
   b      1      10
   c      1      40
   d      1      30
Output: Following is maximum profit sequence of jobs:
       c, a

Input: Five Jobs with following deadlines and profits
JobID Deadline Profit
   a     2       100
   b     1       19
   c     2       27
   d     1       25
   e     3       15
Output: Following is maximum profit sequence of jobs:
       c, a, e

这里已经讨论了时间复杂度为 O(n 2 ) 的贪婪解决方案。下面是简单的贪心算法。

  1. 按利润降序对所有工作进行排序。
  2. 将结果序列初始化为排序作业中的第一个作业。
  3. 为剩余的 n-1 个工作执行以下操作
    • 如果当前作业可以适应当前结果序列而不会错过截止日期,则将当前作业添加到结果中。否则忽略当前作业。

Greedy 解决方案中代价高昂的操作是为作业分配一个空闲槽。我们正在遍历一个工作的每个时隙并分配可用的最大可能时间段(最大时段是什么意思?
假设作业 J1 的截止时间为 t = 5。我们为该作业分配空闲且小于截止时间的最大时间段,即 4-5。现在另一个截止日期为 5 的作业 J2 进来了,因此分配的时间段将是 3-4,因为 4-5 已经分配给作业 J1。
为什么要为工作分配最大的时间段(免费)?
现在我们分配最大可能的时间段,因为如果我们分配的时间段比可用的时间段还要短,那么可能会有其他一些工作错过截止日期。

例子:
J1 截止日期 d1 = 5,利润 40
J2,截止日期 d2 = 1,利润 20
假设我们为作业 J1 分配了 0-1 的时隙。现在无法执行作业 J2,因为我们将在该时间段内执行作业 J1。
使用不相交集进行作业排序
所有时隙最初都是单独的设置。我们首先找到所有作业的最大截止日期。设最大期限为 m。我们创建了 m+1 个单独的集合。如果作业被分配了 t 的时间段,其中 t >= 0,则作业将在 [t-1, t] 期间进行调度。因此,值为 X 的集合表示时隙 [X-1, X]。
我们需要跟踪可以分配给具有截止日期的给定作业的最大可用时间段。为此,我们使用 Disjoint Set Data 结构的父数组。树的根总是最新的可用槽。如果对于一个deadline d,没有slot 可用,那么root 将为0。下面是详细步骤。
初始化不相交集创建初始不相交集。

// m is maximum deadline of a job
parent = new int[m + 1];

// Every node is a parent of itself
for (int i = 0; i ≤ m; i++)
    parent[i] = i;

查找:查找可用的最新时间段。

// Returns the maximum available time slot
find(s)
{
    // Base case
    if (s == parent[s])
       return s;

    // Recursive call with path compression
    return parent[s] = find(parent[s]);
} 

联盟:

Merges two sets.  
// Makes u as parent of v.
union(u, v)
{
   // update the greatest available
   // free slot to u
   parent[v] = u;
} 

find 如何返回最新的可用时间段?
最初所有时隙都是单独的时隙。所以返回的时隙总是最大的。当我们为一个工作分配一个时间段“t”时,我们以“t-1”成为“t”的父项的方式将“t”与“t-1”结合起来。为此,我们调用 union(t-1, t)。这意味着对时隙 t 的所有未来查询现在将返回可用于由 t-1 表示的集合的最新时隙。

执行 :
下面是上述算法的实现。

C++
// C++ Program to find the maximum profit job sequence
// from a given array of jobs with deadlines and profits
#include
using namespace std;
 
// A structure to represent various attributes of a Job
struct Job
{
    // Each job has id, deadline and profit
    char id;
    int deadLine, profit;
};
 
// A Simple Disjoint Set Data Structure
struct DisjointSet
{
    int *parent;
 
    // Constructor
    DisjointSet(int n)
    {
        parent = new int[n+1];
 
        // Every node is a parent of itself
        for (int i = 0; i <= n; i++)
            parent[i] = i;
    }
 
    // Path Compression
    int find(int s)
    {
        /* Make the parent of the nodes in the path
           from u--> parent[u] point to parent[u] */
        if (s == parent[s])
            return s;
        return parent[s] = find(parent[s]);
    }
 
    // Makes u as parent of v.
    void merge(int u, int v)
    {
        //update the greatest available
        //free slot to u
        parent[v] = u;
    }
};
 
// Used to sort in descending order on the basis
// of profit for each job
bool cmp(Job a, Job b)
{
    return (a.profit > b.profit);
}
 
// Functions returns the maximum deadline from the set
// of jobs
int findMaxDeadline(struct Job arr[], int n)
{
    int ans = INT_MIN;
    for (int i = 0; i < n; i++)
        ans = max(ans, arr[i].deadLine);
    return ans;
}
 
int printJobScheduling(Job arr[], int n)
{
    // Sort Jobs in descending order on the basis
    // of their profit
    sort(arr, arr + n, cmp);
 
    // Find the maximum deadline among all jobs and
    // create a disjoint set data structure with
    // maxDeadline disjoint sets initially.
    int maxDeadline = findMaxDeadline(arr, n);
    DisjointSet ds(maxDeadline);
 
    // Traverse through all the jobs
    for (int i = 0; i < n; i++)
    {
        // Find the maximum available free slot for
        // this job (corresponding to its deadline)
        int availableSlot = ds.find(arr[i].deadLine);
 
        // If maximum available free slot is greater
        // than 0, then free slot available
        if (availableSlot > 0)
        {
            // This slot is taken by this job 'i'
            // so we need to update the greatest
            // free slot. Note that, in merge, we
            // make first parameter as parent of
            // second parameter. So future queries
            // for availableSlot will return maximum
            // available slot in set of
            // "availableSlot - 1"
            ds.merge(ds.find(availableSlot - 1),
                             availableSlot);
 
            cout << arr[i].id << " ";
        }
    }
}
 
// Driver code
int main()
{
    Job arr[] =  { { 'a', 2, 100 }, { 'b', 1, 19 },
                   { 'c', 2, 27 },  { 'd', 1, 25 },
                   { 'e', 3, 15 } };
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << "Following jobs need to be "
         << "executed for maximum profit\n";
    printJobScheduling(arr, n);
    return 0;
}


Java
// Java program to find the maximum profit job sequence
// from a given array of jobs with deadlines and profits
import java.util.*;
 
// A Simple Disjoint Set Data Structure
class DisjointSet
{
    int parent[];
 
    // Constructor
    DisjointSet(int n)
    {
        parent = new int[n + 1];
 
        // Every node is a parent of itself
        for (int i = 0; i <= n; i++)
            parent[i] = i;
    }
 
    // Path Compression
    int find(int s)
    {
        /* Make the parent of the nodes in the path
           from u--> parent[u] point to parent[u] */
        if (s == parent[s])
            return s;
        return parent[s] = find(parent[s]);
    }
 
    // Makes u as parent of v.
    void merge(int u, int v)
    {
        //update the greatest available
        //free slot to u
        parent[v] = u;
    }
}
 
class Job implements Comparator
{
    // Each job has a unique-id, profit and deadline
    char id;
    int deadline, profit;
 
    // Constructors
    public Job() { }
    public Job(char id,int deadline,int profit)
    {
        this.id = id;
        this.deadline = deadline;
        this.profit = profit;
    }
 
    // Returns the maximum deadline from the set of jobs
    public static int findMaxDeadline(ArrayList arr)
    {
        int ans = Integer.MIN_VALUE;
        for (Job temp : arr)
            ans = Math.max(temp.deadline, ans);
        return ans;
    }
 
    // Prints optimal job sequence
    public static void printJobScheduling(ArrayList arr)
    {
        // Sort Jobs in descending order on the basis
        // of their profit
        Collections.sort(arr, new Job());
 
        // Find the maximum deadline among all jobs and
        // create a disjoint set data structure with
        // maxDeadline disjoint sets initially.
        int maxDeadline = findMaxDeadline(arr);
        DisjointSet dsu = new DisjointSet(maxDeadline);
 
        // Traverse through all the jobs
        for (Job temp : arr)
        {
            // Find the maximum available free slot for
            // this job (corresponding to its deadline)
            int availableSlot = dsu.find(temp.deadline);
 
 
            // If maximum available free slot is greater
            // than 0, then free slot available
            if (availableSlot > 0)
            {
                // This slot is taken by this job 'i'
                // so we need to update the greatest free
                // slot. Note that, in merge, we make
                // first parameter as parent of second
                // parameter.  So future queries for
                // availableSlot will return maximum slot
                // from set of "availableSlot - 1"
                dsu.merge(dsu.find(availableSlot - 1),
                                   availableSlot);
                System.out.print(temp.id + " ");
            }
        }
        System.out.println();
    }
 
    // Used to sort in descending order on the basis
    // of profit for each job
    public int compare(Job j1, Job j2)
    {
        return j1.profit > j2.profit? -1: 1;
    }
}
 
// Driver code
class Main
{
    public static void main(String args[])
    {
        ArrayList arr=new ArrayList();
        arr.add(new Job('a',2,100));
        arr.add(new Job('b',1,19));
        arr.add(new Job('c',2,27));
        arr.add(new Job('d',1,25));
        arr.add(new Job('e',3,15));
        System.out.println("Following jobs need to be "+
                           "executed for maximum profit");
        Job.printJobScheduling(arr);
    }
}


Python3
# Python3 program to find the maximum profit
# job sequence from a given array of jobs
# with deadlines and profits
import sys
 
class DisjointSet:
    def __init__(self, n):
        self.parent = [i for i in range(n + 1)]
 
    def find(self, s):
     
        # Make the parent of nodes in the path from
        # u --> parent[u] point to parent[u]
        if s == self.parent[s]:
            return s
        self.parent[s] = self.find(self.parent[s])
        return self.parent[s]
 
    # Make us as parent of v
    def merge(self, u, v):
         
        # Update the greatest available
        # free slot to u
        self.parent[v] = u
 
def cmp(a):
    return a['profit']
 
def findmaxdeadline(arr, n):
    """
    :param arr: Job array
    :param n: length of array
    :return: maximum deadline from the set of jobs
    """
    ans = - sys.maxsize - 1
    for i in range(n):
        ans = max(ans, arr[i]['deadline'])
    return ans
 
def printjobscheduling(arr, n):
     
    # Sort jobs in descending order on
    # basis of their profit
    arr = sorted(arr, key = cmp, reverse = True)
 
    """
    Find the maximum deadline among all jobs and
    create a disjoint set data structure with
    max_deadline disjoint sets initially
    """
    max_deadline = findmaxdeadline(arr, n)
    ds = DisjointSet(max_deadline)
 
    for i in range(n):
 
        # find maximum available free slot for
        # this job (corresponding to its deadline)
        available_slot = ds.find(arr[i]['deadline'])
        if available_slot > 0:
 
            # This slot is taken by this job 'i'
            # so we need to update the greatest free slot.
            # Note: In merge, we make first parameter
            # as parent of second parameter.
            # So future queries for available_slot will
            # return maximum available slot in set of
            # "available_slot - 1"
            ds.merge(ds.find(available_slot - 1),
                             available_slot)
            print(arr[i]['id'], end = " ")
 
# Driver Code
if __name__ == "__main__":
    arr = [{'id': 'a', 'deadline': 2, 'profit': 100},
           {'id': 'b', 'deadline': 1, 'profit': 19},
           {'id': 'c', 'deadline': 2, 'profit': 27},
           {'id': 'd', 'deadline': 1, 'profit': 25},
           {'id': 'e', 'deadline': 3, 'profit': 15}]
    n = len(arr)
    print("Following jobs need to be",
          "executed for maximum profit")
    printjobscheduling(arr, n)
 
# This code is contributed by Rajat Srivastava


输出
Following jobs need to be executed for maximum profit
a c e 

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程