给定N个工作,其中每个工作都由以下三个元素表示。
- 开始时间
- 结束时间
- 关联的利润或价值(> = 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++
// 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;
}
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
输出:
The optimal profit is 250
上述解决方案可能包含许多重叠的子问题。例如,如果lastNonConflicting()始终返回上一个作业,则findMaxProfitRec(arr,n-1)被调用两次,时间复杂度变为O(n * 2 n )。作为另一个示例,当lastNonConflicting()返回上一个作业的前一个时,有两个递归调用,分别是n-2和n-1。在此示例情况下,递归变得与斐波纳契数相同。
因此,此问题同时具有动态规划,最优子结构和重叠子问题的属性。
像其他动态编程问题一样,我们可以通过创建一个存储子问题解决方案的表来解决此问题。
下面是基于动态编程的C++实现。
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
输出:
The optimal profit is 250
上述动态规划解决方案的时间复杂度为O(n 2 )。请注意,可以使用latestNonConflict()中的二进制搜索而不是线性搜索将上述解决方案优化为O(nLogn)。感谢Garvit提出此优化建议。请参阅以下帖子以获取详细信息。
O(n Log n)时间的加权作业调度