贪心算法需求:
- 给定一个任务调度问题,每个任务有一个开始时间、结束时间和收益,使用贪心算法找到能获得最大收益的任务子集,且任务之间不能有时间冲突。
- 有一个背包,背包容量为 C,每个物品有重量 w 和价值 v,利用贪心算法确定在不超过背包容量的情况下,能装入背包的物品组合,使得总价值最大(这里可以是 0-1 背包问题或者部分背包问题)。
- 在一个图中,每个边有一个权重,使用贪心算法实现最小生成树算法(如 Prim 算法或 Kruskal 算法),找到连接图中所有顶点的边的子集,使得这些边的权重之和最小。
- 有一组活动,每个活动有一个开始时间和结束时间,利用贪心算法找到能在一个时间段内安排最多活动的方案。
- 对一个整数数组进行排序,使用贪心算法将数组中的元素分成若干组,使得每组元素的和尽可能接近。
1. 任务调度问题
任务调度问题需要找到能获得最大收益的任务子集,且任务之间不能有时间冲突。我们可以先按结束时间对任务进行排序,然后依次选择不冲突的任务。 def job_scheduling(start_time, end_time, profit): jobs = sorted(zip(start_time, end_time, profit), key=lambda x: x[1]) n = len(jobs) dp = [0] * n dp[0] = jobs[0][2]
for i in range(1, n):
incl_prof = jobs[i][2]
l = 0
r = i - 1
last_non_conflict = -1
while l <= r:
mid = (l + r) // 2
if jobs[mid][1] <= jobs[i][0]:
last_non_conflict = mid
l = mid + 1
else:
r = mid - 1
if last_non_conflict != -1:
incl_prof += dp[last_non_conflict]
dp[i] = max(incl_prof, dp[i - 1])
return dp[-1]
示例使用
start_time = [1, 2, 3, 4, 6] end_time = [3, 5, 9, 6, 9] profit = [20, 20, 100, 70, 60] print(job_scheduling(start_time, end_time, profit))
2. 背包问题
这里我们实现部分背包问题,按单位重量价值对物品进行排序,然后依次选择单位重量价值高的物品。 def fractional_knapsack(capacity, weights, values):
items = [(v / w, w, v) for w, v in zip(weights, values)]
items.sort(reverse=True)
total_value = 0
for ratio, weight, value in items:
if capacity == 0:
return total_value
amount = min(weight, capacity)
total_value += amount * ratio
capacity -= amount
return total_value
示例使用
capacity = 50 weights = [10, 20, 30] values = [60, 100, 120] print(fractional_knapsack(capacity, weights, values))
3. 最小生成树问题(Kruskal 算法)
Kruskal 算法通过按边的权重排序,然后依次选择不形成环的边来构建最小生成树。 class UnionFind: def init(self, n): self.parent = list(range(n)) self.rank = [0] * n
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
root_x = self.find(x)
root_y = self.find(y)
if root_x != root_y:
if self.rank[root_x] > self.rank[root_y]:
self.parent[root_y] = root_x
elif self.rank[root_x] < self.rank[root_y]:
self.parent[root_x] = root_y
else:
self.parent[root_y] = root_x
self.rank[root_x] += 1
return True
return False
def kruskal(edges, n): edges.sort(key=lambda x: x[2]) uf = UnionFind(n) mst = [] for u, v, w in edges: if uf.union(u, v): mst.append((u, v, w)) return mst
示例使用
edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)] n = 4 print(kruskal(edges, n))
4. 活动选择问题
活动选择问题需要找到能在一个时间段内安排最多活动的方案。我们可以按结束时间对活动进行排序,然后依次选择不冲突的活动。
def activity_selection(start, end):
activities = sorted(zip(start, end), key=lambda x: x[1])
result = []
i = 0
result.append(activities[i])
for j in range(1, len(activities)):
if activities[j][0] >= activities[i][1]:
result.append(activities[j])
i = j
return result
示例使用
start = [1, 3, 0, 5, 8, 5] end = [2, 4, 6, 7, 9, 9] print(activity_selection(start, end))
5. 数组分组问题
将数组中的元素分成若干组,使得每组元素的和尽可能接近。我们可以先对数组进行排序,然后依次将元素添加到和最小的组中。
def group_array(arr, num_groups): arr.sort(reverse=True) groups = [[] for _ in range(num_groups)] sums = [0] * num_groups
for num in arr:
min_index = sums.index(min(sums))
groups[min_index].append(num)
sums[min_index] += num
return groups
示例使用
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9] num_groups = 3 print(group_array(arr, num_groups))