目录
1.需要知识
1.1 sort自定义排序
sort函数有三个参数:
sort(start, end, compare);start为需要排序区域的起点;
end为需要排序区域的终点;
compare为排序方式,可以不指定,此时默认为升序排列。(ps:这个也可以我们自行定义)
如果要自行定义的话,如下:
//自定义的compare函数,按照结束时间早晚升序排序
bool lessSort(array<int,3> a,array<int,3> b){return a[1] < b[1];}
class Solution {
public:
int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
int n = startTime.size();
array<int,3> res[n];
for(int i = 0;i<n;i++){
res[i]={startTime[i],endTime[i],profit[i]};
}
sort(res,res+n,lessSort);
}
};
还有一个关于自定义的compare函数(也就是我上面的lessSort函数)要注意的点:
sort的第三个参数是一个函数指针,如果将自定义的compare函数放在类里面,那么此时compare是一个非静态函数指针,会报错。
需要将compare()函数的声明加static关键字或者将函数移到类外即可
1.2 upper_bound()
upper_bound() 函数定义在<algorithm>
头文件中,用于在指定范围内查找大于(没有等于!!!)目标值的第一个元素(使用二分查找法)。该函数的语法格式有 2 种,分别是:
//查找[first, last)区域中第一个大于 val 的元素。 ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val); //查找[first, last)区域中第一个不符合 comp 规则的元素 ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
其中,
- first 和 last 都为正向迭代器,[first, last) 用于指定该函数的作用范围;
- val 用于执行目标值;
- comp 用于自定义查找规则:此函数中可接收一个包含 2 个形参(第一个形参值始终为 val)且返回值为 bool 类型的函数,可以是普通函数,也可以是函数对象。
意思也就是说,在使用自定义的comp函数比较中,comp(val,比较的数组中的值)
举例:
bool compaire(int a, int b) { return a < b; }//升序排序
int main() {
int a[10] = { 2,2,3,4,5,6,7,8,9,10 };
// 使用自定义compaire比较,那么在比较中就是compaire(1,a[i]) 其中1也就是传入的val值
cout << upper_bound(a, a + 9, 1,compaire)-a << endl;
return 0;
}
输出结果:
0
因为相当于 1和2,2,3,4,5,6,7,8,9比较,得到 a[0] = 2 > 1 是第一个 >1 的元素
注:最后upper_bound返回的是一个指向找到元素的指针,如果一直没找到,就会返回指向最后一个查找元素的指针
2. 题目
你打算利用空闲时间来做兼职工作赚些零花钱。
这里有 n
份兼职工作,每份工作预计从 startTime[i]
开始到 endTime[i]
结束,报酬为 profit[i]
。
给你一份兼职工作表,包含开始时间 startTime
,结束时间 endTime
和预计报酬 profit
三个数组,请你计算并返回可以获得的最大报酬。
注意,时间上出现重叠的 2 份工作不能同时进行。
如果你选择的工作在时间 X
结束,那么你可以立刻进行在时间 X
开始的下一份工作。
示例 1:
输入:startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70] 输出:120 解释: 我们选出第 1 份和第 4 份工作, 时间范围是 [1-3]+[3-6],共获得报酬 120 = 50 + 70。
示例 2:
输入:startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60] 输出:150 解释: 我们选择第 1,4,5 份工作。 共获得报酬 150 = 20 + 70 + 60。
示例 3:
输入:startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4] 输出:6s
3. 思路
- 将所有的兼职工作按照结束时间早晚排列
- 利用动态规划,选择当前的兼职工作是加入还是不加入;
- 加入:找到最近的结束在当前的兼职活动开始前的活动(before),那么加入当前兼职工作后的收益就是dp [before] + profit[current]
- 不加入:当前收益是上一个兼职工作的收益(注意:不是上述加入中的before兼职工作的收益,而是排序中的上一个兼职工作)
比较加入和不加入的收益大小,取大的加入dp数组中记录
也就是
dp[ current ] = max ( dp [current-1] , dp [before] + profit [current] )
但是程序中是从下标0开始遍历兼职活动的,所以为了处理 0 元素使用 dp 时出现下标 -1 的情况,将dp中的元素都统一 +1,赋值dp[0] 为0,并且这样以后,所有没有上一个兼职活动的活动,也就是
endTime[before] <= startTime[current] 的元素个数为0个
那么使用upper_bound()查找的时候,就会返回第一个元素的编号,也就是0,(不明白的小可爱可以看看上面upper_bound()的例子)但我们处理过dp[0] = 0,那么这一类的活动就都会有一个dp[0] = 0 的收益,不需要进行特殊情况处理
4. 代码实现
bool lessSort(array<int,3> a,array<int,3> b){return a[1] < b[1];}//升序排序
class Solution {
public:
int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
int n = startTime.size();
//array<int,3> res[n] 存放兼职活动的数组,每个兼职活动中有3个元素,为开始时间,结束时间和收益
array<int,3> res* = new array<int,3>[n];
int dp* = new int[n];
for(int i = 0;i<n;i++){
res[i]={startTime[i],endTime[i],profit[i]};
}
//对res数组进行按照结束时间的早晚排序,此处要自定义sort中的compare
sort(res,res+n,lessSort);
dp[0] = 0;
for(int i = 0;i<n;i++){
//选择加入该兼职活动,需要查找最近的endTime在该兼职活动startTime前的活动,利用upper_bound得到那个活动
int r = upper_bound(res,res+i,array<int,3>{0,res[i][0],0},lessSort)-res;
dp[i+1] = max(dp[i],dp[r]+res[i][2]);
}
return dp[n];
}
};
5. 后记
在查看大家题解的时候,还发现了一种很有意思的sort中compare的写法,记录一下:
//这样写就不需要再写上面的lessSort函数了
sort(jobs, jobs + n, [](auto &a, auto &b) { return a[1] < b[1]; }); // 按照结束时间排序