📜  加权作业调度

📅  最后修改于: 2021-09-17 07:02:45             🧑  作者: Mango

给定 N 个工作,其中每个工作都由以下三个元素表示。

  1. 开始时间
  2. 完成时间
  3. 利润或价值相关 (>= 0)

找到工作的最大利润子集,使得该子集中没有两个工作重叠。

例子:

Input: Number of Jobs n = 4
       Job Details {Start Time, Finish Time, Profit}
       Job 1:  {1, 2, 50} 
       Job 2:  {3, 5, 20}
       Job 3:  {6, 19, 100}
       Job 4:  {2, 100, 200}
Output: The maximum profit is 250.
We can get the maximum profit by scheduling jobs 1 and 4.
Note that there is longer schedules possible Jobs 1, 2 and 3 
but the profit with this schedule is 20+50+100 which is less than 250.

这里讨论了这个问题的一个简单版本,其中每个工作都有相同的利润或价值。活动选择的贪婪策略在这里不起作用,因为具有更多工作的时间表可能具有较小的利润或价值。

上述问题可以使用以下递归解决方案来解决。

1) First sort jobs according to finish time.
2) Now apply following recursive process. 
   // Here arr[] is array of n jobs
   findMaximumProfit(arr[], n)
   {
     a) if (n == 1) return arr[0];
     b) Return the maximum of following two profits.
         (i) Maximum profit by excluding current job, i.e., 
             findMaximumProfit(arr, n-1)
         (ii) Maximum profit by including the current job            
   }

How to find the profit including current job?
The idea is to find the latest job before the current job (in 
sorted array) that doesn't conflict with current job 'arr[n-1]'. 
Once we find such a job, we recur for all jobs till that job and
add profit of current job to result.
In the above example, "job 1" is the latest non-conflicting
for "job 4" and "job 2" is the latest non-conflicting for "job 3".

以下是上述朴素递归方法的实现。

C++
// C++ program for weighted job scheduling using Naive Recursive Method
#include 
#include 
using namespace std;
 
// A job has start time, finish time and profit.
struct Job
{
    int start, finish, profit;
};
 
// A utility function that is used for sorting events
// according to finish time
bool jobComparataor(Job s1, Job s2)
{
    return (s1.finish < s2.finish);
}
 
// Find the latest job (in sorted array) that doesn't
// conflict with the job[i]. If there is no compatible job,
// then it returns -1.
int latestNonConflict(Job arr[], int i)
{
    for (int j=i-1; j>=0; j--)
    {
        if (arr[j].finish <= arr[i-1].start)
            return j;
    }
    return -1;
}
 
// A recursive function that returns the maximum possible
// profit from given array of jobs.  The array of jobs must
// be sorted according to finish time.
int findMaxProfitRec(Job arr[], int n)
{
    // Base case
    if (n == 1) return arr[n-1].profit;
 
    // Find profit when current job is included
    int inclProf = arr[n-1].profit;
    int i = latestNonConflict(arr, n);
    if (i != -1)
      inclProf += findMaxProfitRec(arr, i+1);
 
    // Find profit when current job is excluded
    int exclProf = findMaxProfitRec(arr, n-1);
 
    return max(inclProf,  exclProf);
}
 
// The main function that returns the maximum possible
// profit from given array of jobs
int findMaxProfit(Job arr[], int n)
{
    // Sort jobs according to finish time
    sort(arr, arr+n, jobComparataor);
 
    return findMaxProfitRec(arr, n);
}
 
// Driver program
int main()
{
    Job arr[] = {{3, 10, 20}, {1, 2, 50}, {6, 19, 100}, {2, 100, 200}};
    int n = sizeof(arr)/sizeof(arr[0]);
    cout << "The optimal profit is " << findMaxProfit(arr, n);
    return 0;
}


Python3
# Python3 program for weighted job scheduling using
# Naive Recursive Method
 
# Importing the following module to sort array
# based on our custom comparison function
from functools import cmp_to_key
 
# A job has start time, finish time and profit
class Job:
     
    def __init__(self, start, finish, profit):
         
        self.start = start
        self.finish = finish
        self.profit = profit
 
# A utility function that is used for
# sorting events according to finish time
def jobComparataor(s1, s2):
     
    return s1.finish < s2.finish
 
# Find the latest job (in sorted array) that
# doesn't conflict with the job[i]. If there
# is no compatible job, then it returns -1
def latestNonConflict(arr, i):
     
    for j in range(i - 1, -1, -1):
        if arr[j].finish <= arr[i - 1].start:
            return j
             
    return -1
 
# A recursive function that returns the
# maximum possible profit from given
# array of jobs. The array of jobs must
# be sorted according to finish time
def findMaxProfitRec(arr, n):
     
    # Base case
    if n == 1:
        return arr[n - 1].profit
 
    # Find profit when current job is included
    inclProf = arr[n - 1].profit
    i = latestNonConflict(arr, n)
     
    if i != -1:
        inclProf += findMaxProfitRec(arr, i + 1)
 
    # Find profit when current job is excluded
    exclProf = findMaxProfitRec(arr, n - 1)
    return max(inclProf, exclProf)
 
# The main function that returns the maximum
# possible profit from given array of jobs
def findMaxProfit(arr, n):
     
    # Sort jobs according to finish time
    arr = sorted(arr, key = cmp_to_key(jobComparataor))
    return findMaxProfitRec(arr, n)
 
# Driver code
values = [ (3, 10, 20), (1, 2, 50),
           (6, 19, 100), (2, 100, 200) ]
arr = []
for i in values:
    arr.append(Job(i[0], i[1], i[2]))
     
n = len(arr)
 
print("The optimal profit is", findMaxProfit(arr, n))
 
# This code is code contributed by Kevin Joshi


C++
// C++ program for weighted job scheduling using Dynamic Programming.
#include 
#include 
using namespace std;
 
// A job has start time, finish time and profit.
struct Job
{
    int start, finish, profit;
};
 
// A utility function that is used for sorting events
// according to finish time
bool jobComparataor(Job s1, Job s2)
{
    return (s1.finish < s2.finish);
}
 
// Find the latest job (in sorted array) that doesn't
// conflict with the job[i]
int latestNonConflict(Job arr[], int i)
{
    for (int j=i-1; j>=0; j--)
    {
        if (arr[j].finish <= arr[i].start)
            return j;
    }
    return -1;
}
 
// The main function that returns the maximum possible
// profit from given array of jobs
int findMaxProfit(Job arr[], int n)
{
    // Sort jobs according to finish time
    sort(arr, arr+n, jobComparataor);
 
    // Create an array to store solutions of subproblems.  table[i]
    // stores the profit for jobs till arr[i] (including arr[i])
    int *table = new int[n];
    table[0] = arr[0].profit;
 
    // Fill entries in M[] using recursive property
    for (int i=1; i


Python3
# Python3 program for weighted job scheduling
# using Dynamic Programming
 
# Importing the following module to sort array
# based on our custom comparison function
from functools import cmp_to_key
 
# A job has start time, finish time and profit
class Job:
     
    def __init__(self, start, finish, profit):
         
        self.start = start
        self.finish = finish
        self.profit = profit
 
# A utility function that is used for sorting
# events according to finish time
def jobComparataor(s1, s2):
     
    return s1.finish < s2.finish
 
# Find the latest job (in sorted array) that
# doesn't conflict with the job[i]. If there
# is no compatible job, then it returns -1
def latestNonConflict(arr, i):
     
    for j in range(i - 1, -1, -1):
        if arr[j].finish <= arr[i - 1].start:
            return j
             
    return -1
 
# The main function that returns the maximum possible
# profit from given array of jobs
def findMaxProfit(arr, n):
     
    # Sort jobs according to finish time
    arr = sorted(arr, key = cmp_to_key(jobComparataor))
 
    # Create an array to store solutions of subproblems.
    # table[i] stores the profit for jobs till arr[i]
    # (including arr[i])
    table = [None] * n
    table[0] = arr[0].profit
 
    # Fill entries in M[] using recursive property
    for i in range(1, n):
         
        # Find profit including the current job
        inclProf = arr[i].profit
        l = latestNonConflict(arr, i)
         
        if l != -1:
            inclProf += table[l]
 
        # Store maximum of including and excluding
        table[i] = max(inclProf, table[i - 1])
 
    # Store result and free dynamic memory
    # allocated for table[]
    result = table[n - 1]
 
    return result
 
# Driver code
values = [ (3, 10, 20), (1, 2, 50),
           (6, 19, 100), (2, 100, 200) ]
arr = []
for i in values:
    arr.append(Job(i[0], i[1], i[2]))
     
n = len(arr)
 
print("The optimal profit is", findMaxProfit(arr, n))
 
# This code is contributed by Kevin Joshi


输出:

The optimal profit is 250

上述解决方案可能包含许多重叠的子问题。例如,如果 lastNonConflicting() 总是返回上一个作业,则 findMaxProfitRec(arr, n-1) 被调用两次,时间复杂度变为 O(n*2 n )。作为另一个例子,当 lastNonConflicting() 返回前一个作业时,有两个递归调用,分别是 n-2 和 n-1。在这个例子中,递归变得与斐波那契数相同。

所以这个问题同时具有动态规划、最优子结构和重叠子问题的性质。
像其他动态规划问题一样,我们可以通过制作一个存储子问题解的表来解决这个问题。

下面是基于动态规划的实现。

C++

// C++ program for weighted job scheduling using Dynamic Programming.
#include 
#include 
using namespace std;
 
// A job has start time, finish time and profit.
struct Job
{
    int start, finish, profit;
};
 
// A utility function that is used for sorting events
// according to finish time
bool jobComparataor(Job s1, Job s2)
{
    return (s1.finish < s2.finish);
}
 
// Find the latest job (in sorted array) that doesn't
// conflict with the job[i]
int latestNonConflict(Job arr[], int i)
{
    for (int j=i-1; j>=0; j--)
    {
        if (arr[j].finish <= arr[i].start)
            return j;
    }
    return -1;
}
 
// The main function that returns the maximum possible
// profit from given array of jobs
int findMaxProfit(Job arr[], int n)
{
    // Sort jobs according to finish time
    sort(arr, arr+n, jobComparataor);
 
    // Create an array to store solutions of subproblems.  table[i]
    // stores the profit for jobs till arr[i] (including arr[i])
    int *table = new int[n];
    table[0] = arr[0].profit;
 
    // Fill entries in M[] using recursive property
    for (int i=1; i

蟒蛇3

# Python3 program for weighted job scheduling
# using Dynamic Programming
 
# Importing the following module to sort array
# based on our custom comparison function
from functools import cmp_to_key
 
# A job has start time, finish time and profit
class Job:
     
    def __init__(self, start, finish, profit):
         
        self.start = start
        self.finish = finish
        self.profit = profit
 
# A utility function that is used for sorting
# events according to finish time
def jobComparataor(s1, s2):
     
    return s1.finish < s2.finish
 
# Find the latest job (in sorted array) that
# doesn't conflict with the job[i]. If there
# is no compatible job, then it returns -1
def latestNonConflict(arr, i):
     
    for j in range(i - 1, -1, -1):
        if arr[j].finish <= arr[i - 1].start:
            return j
             
    return -1
 
# The main function that returns the maximum possible
# profit from given array of jobs
def findMaxProfit(arr, n):
     
    # Sort jobs according to finish time
    arr = sorted(arr, key = cmp_to_key(jobComparataor))
 
    # Create an array to store solutions of subproblems.
    # table[i] stores the profit for jobs till arr[i]
    # (including arr[i])
    table = [None] * n
    table[0] = arr[0].profit
 
    # Fill entries in M[] using recursive property
    for i in range(1, n):
         
        # Find profit including the current job
        inclProf = arr[i].profit
        l = latestNonConflict(arr, i)
         
        if l != -1:
            inclProf += table[l]
 
        # Store maximum of including and excluding
        table[i] = max(inclProf, table[i - 1])
 
    # Store result and free dynamic memory
    # allocated for table[]
    result = table[n - 1]
 
    return result
 
# Driver code
values = [ (3, 10, 20), (1, 2, 50),
           (6, 19, 100), (2, 100, 200) ]
arr = []
for i in values:
    arr.append(Job(i[0], i[1], i[2]))
     
n = len(arr)
 
print("The optimal profit is", findMaxProfit(arr, n))
 
# This code is contributed by Kevin Joshi

输出:

The optimal profit is 250

上述动态规划解决方案的时间复杂度为 O(n 2 )。请注意,可以使用 latestNonConflict() 中的二进制搜索而不是线性搜索将上述解决方案优化为 O(nLogn)。感谢 Garvit 提出这个优化建议。详情请参阅以下帖子。

O(n Log n) 时间内的加权作业调度

参考:
http://courses.cs.washington.edu/courses/cse521/13wi/slides/06dp-sched.pdf

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