最大利润分配问题
题目描述
你有 n
个工作和 m
个工人。给定三个数组:difficulty
、profit
和 worker
,其中:
difficulty[i]
表示第i
个工作的难度,profit[i]
表示第i
个工作的收益,worker[i]
是第i
个工人的能力,即该工人只能完成难度小于等于worker[i]
的工作。
每个工人最多只能安排一个工作,但一个工作可以由多个工人完成。目标是将工人分配到工作岗位后,最大化总收益。
解题思路
预处理
-
排序工作:为了方便查找每个工人能完成的最难工作及其对应的收益,我们首先按照工作的难度进行排序。
-
构建最大收益数组:创建一个数组
maxProfitAtDifficulty
,该数组记录了在每一个工作难度下能够获得的最大收益。这样我们可以快速查询在任意难度范围内的最大收益。
分配工人
-
排序工人:将工人的能力进行排序,方便按照能力逐一处理。
-
计算总收益:遍历排序后的工人数组,利用
maxProfitAtDifficulty
数组,通过二分查找或线性扫描快速找到每个工人能够完成的最难工作,并获取对应的最大收益,累加到总收益中。
复杂度分析
- 排序工作的复杂度:
O(n log n)
,其中n
是工作的数量。 - 构建最大收益数组的复杂度:
O(n)
。 - 排序工人的复杂度:
O(m log m)
,其中m
是工人的数量。 - 计算总收益的复杂度:
O(m + n)
,其中线性扫描部分总共最多执行n
次。
因此,总的时间复杂度为 O(n log n + m log m)
。
代码实现
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int difficulty;
int profit;
} Job;
int cmp(const void *a, const void *b) {
return ((Job *)a)->difficulty - ((Job *)b)->difficulty;
}
int maxProfitAssignment(int* difficulty, int difficultySize, int* profit, int profitSize, int* worker, int workerSize) {
// 先把所有工作按难度排序
Job* jobs = (Job*)malloc(difficultySize * sizeof(Job));
for (int i = 0; i < difficultySize; ++i) {
jobs[i].difficulty = difficulty[i];
jobs[i].profit = profit[i];
}
qsort(jobs, difficultySize, sizeof(Job), cmp);
// 创建并填充 maxProfitAtDifficulty 数组
int* maxProfitAtDifficulty = (int*)malloc(difficultySize * sizeof(int));
maxProfitAtDifficulty[0] = jobs[0].profit;
for (int i = 1; i < difficultySize; ++i) {
maxProfitAtDifficulty[i] = jobs[i].profit > maxProfitAtDifficulty[i-1] ? jobs[i].profit : maxProfitAtDifficulty[i-1];
}
// 对每个工人的能力进行排序
qsort(worker, workerSize, sizeof(int), cmp);
// 计算总收益
int totalProfit = 0;
int jobIndex = 0;
for (int i = 0; i < workerSize; ++i) {
// 找到当前工人能胜任的最难的工作
while (jobIndex < difficultySize && worker[i] >= jobs[jobIndex].difficulty) {
++jobIndex;
}
if (jobIndex > 0) {
totalProfit += maxProfitAtDifficulty[jobIndex - 1];
}
}
// 释放分配的内存
free(jobs);
free(maxProfitAtDifficulty);
return totalProfit;
}
int main() {
int difficultySize, profitSize, workerSize;
printf("请输入 difficulty 数组的大小: ");
scanf("%d", &difficultySize);
int* difficulty = (int*)malloc(difficultySize * sizeof(int));
printf("请输入 difficulty 数组: ");
getchar(); // 清除换行符
readArray(difficulty, difficultySize);
printf("请输入 profit 数组的大小: ");
scanf("%d", &profitSize);
int* profit = (int*)malloc(profitSize * sizeof(int));
printf("请输入 profit 数组: ");
getchar(); // 清除换行符
readArray(profit, profitSize);
printf("请输入 worker 数组的大小: ");
scanf("%d", &workerSize);
int* worker = (int*)malloc(workerSize * sizeof(int));
printf("请输入 worker 数组: ");
getchar(); // 清除换行符
readArray(worker, workerSize);
int result = maxProfitAssignment(difficulty, difficultySize, profit, profitSize, worker, workerSize);
printf("最大利润为 %d\n", result);
// 释放分配的内存
free(difficulty);
free(profit);
free(worker);
return 0;
}
详细解释
Job
结构体:定义工作对象,包含难度和收益两个字段。- 排序函数
cmp
:用于按难度对Job
进行排序的比较函数。 maxProfitAssignment
函数:主函数,负责计算最大总收益。- 排序工作:使用
qsort
对工作按难度排序。 - 构建
maxProfitAtDifficulty
数组:记录每个难度下的最大收益。 - 排序工人:对工人的能力进行排序。
- 计算总收益:遍历每个工人,查找其可胜任的最难工作并累加收益。
- 排序工作:使用
- 主函数
main
:测试样例,输出最大利润。
总结
该问题通过排序和预处理数组的方式,提高了查找和计算的效率,使得整体解决方案达到了较优的时间复杂度 O(n log n + m log m)
。这对于处理大规模输入数据时非常高效。