引言
区间调度问题是一个经典的算法问题,它广泛应用于任务调度、资源分配等领域。本文将从基础的区间调度问题出发,介绍如何通过贪心算法高效解决这个问题。接着,我们会探讨问题的扩展——加权区间调度问题,并展示如何使用动态规划来解决该问题,以实现权重的最大化。通过对这两种算法的实现和对比,您将理解不同算法的应用场景、优劣以及最优性。
一、问题描述
1.1 经典的区间调度问题
经典的区间调度问题定义如下:
- 我们有 (n) 个请求,每个请求 (i) 具有开始时间 (s(i)) 和结束时间 (f(i)),且 (s(i) < f(i))。
- 两个请求 (i) 和 (j) 是兼容的,若它们的时间区间不重叠,即 (f(i) \leq s(j)) 或 (f(j) \leq s(i))。
- 目标是从这些请求中选择一个最大兼容子集,确保所选请求的时间不重叠,并且尽可能多地选择请求。
例如,给定一组请求:
- 请求1:(s(1) = 1, f(1) = 4)
- 请求2:(s(2) = 3, f(2) = 5)
- 请求3:(s(3) = 0, f(3) = 6)
- 请求4:(s(4) = 5, f(4) = 7)
- 请求5:(s(5) = 8, f(5) = 9)
- 请求6:(s(6) = 5, f(6) = 9)
我们需要找到一个不重叠的最大请求子集。
1.2 加权区间调度问题
加权区间调度问题是经典区间调度问题的扩展:
- 除了每个请求 (i) 具有开始时间 (s(i)) 和结束时间 (f(i)) 之外,每个请求还附带一个权重 (w(i))。
- 目标从这些请求中选择一个最大权重兼容子集,即所有请求不重叠,并且所选请求的总权重最大。
二、贪心算法解决经典区间调度问题
在经典的区间调度问题中,贪心算法表现良好。它的核心思想是:每次选择结束时间最早的请求,这样可以为后续的请求留出更多的时间空间,保证能够选择尽可能多的不重叠请求。
2.1 贪心算法步骤
- 排序:按照请求的结束时间 (f(i)) 对所有请求进行升序排序。
- 选择:每次选择当前结束时间最早的请求,并排除与之重叠的其他请求。
- 重复:继续选择剩余请求中结束时间最早的请求,直到所有请求都处理完。
2.2 Python实现
class Request:
def __init__(self, start, finish):
self.start = start
self.finish = finish
def greedy_interval_scheduling(requests):
# 按结束时间排序
sorted_requests = sorted(requests, key=lambda x: x.finish)
selected_requests = []
last_finish_time = 0
# 选择结束时间最早且不重叠的请求
for request in sorted_requests:
if request.start >= last_finish_time:
selected_requests.append(request)
last_finish_time = request.finish
return selected_requests
# 测试数据
requests = [
Request(1, 4),
Request(3,