二分查找(深刻理解)+动态规划(一维优化技巧 玄学秘籍)

文章讨论了一种解决兼职工作安排问题的方法,通过将工作按结束时间排序并运用二分搜索,以O(nlogn)的时间复杂度寻找每个任务结束后能立即开始的最有利工作,从而实现最大报酬。作者强调了贪心策略的局限性,并展示了如何使用动态规划和二分技巧求解此问题。
摘要由CSDN通过智能技术生成

首先是题目的大致描述:这个理解起来不难(题目读起来越简单,心里越发慌…)
你打算利用空闲时间来做兼职工作赚些零花钱。

这里有 n 份兼职工作,每份工作预计从 startTime[i] 开始到 endTime[i] 结束,报酬为 profit[i]。

给你一份兼职工作表,包含开始时间 startTime,结束时间 endTime 和预计报酬 profit 三个数组,请你计算并返回可以获得的最大报酬。

注意,时间上出现重叠的 2 份工作不能同时进行。

如果你选择的工作在时间 X 结束,那么你可以立刻进行在时间 X 开始的下一份工作。

总结一下原题描述就是:我们需要选择不重复的若干工作进行利润的最大化,类似于工序安排问题,但是这里加了个利润最大化,这题数据范围很玄学是5*10^4,其实看到这个大家肯定能反映过来二维DP肯定是行不通的了,超时的数据范围,那么如果是O(n)的时间复杂度呢,我们有什么好的解决办法吗,其实这题我第一时间想到的是贪心思想,但是想想好像不合理,因为如果按照结束时间排序进行贪心,如果最后一个利润搞的超级大,大过了所有那么肯定是贪心失败,那么如果按照利润进行贪心呢,显然也不行,不能考虑到分配时间的合理性,所以这个题目只能是DP来解决。这类题目最常规的思想就是按照结束时间进行排序,那么我们考虑一下这个有序的结束时间,其实有个思路就是可不可以考虑二分是思想,那么时间复杂度可以优化到O(nlogn)其实这个思路是可行的,我们针对当前的任务的开始时间,找到结束时间最接近该任务的对于其整个利润进行最大化更新是不是就可以得到全局的最优解了,显然是正确的对吧,如果没有找到那么就是当前任务本身的利润和dp[i-1]的利润取最大值,这种就是会兼顾到我前面所说的那个最后一个加入超级大,大过所有的利润和,那么我们就会更新max_profit,所以这个题整个的思想是这么分析的,这里还有个手写二分的技巧,在代码里给大家分析出来了(我们无需进行判断最后的正确结果是存在于l还是r中,我们就搞一个额外变量存一下我们的合理下标即可)

class Solution {
public:
    int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
        int n = startTime.size();
        vector<array<int, 3>> jobs(n);
        for(int i = 0;i < n; i++){
            jobs[i] = {endTime[i], startTime[i], profit[i]};
        }
        sort(jobs.begin(), jobs.end(), [](auto &a, auto &b){return a[0] < b[0];});//这个排序技巧大家可以学习一下,代码很简洁
        vector<int>dp(n + 1);
        dp[1] = jobs[0][2];//初始化第1个任务最大利润就是自身 
        for(int i = 1;i < n; i++){//从第一个任务开始找 
            int l = 0, r = i - 1;//l是找寻范围左边界,r是有边界
            int tmp = jobs[i][1], index = -1;//index保存我们的结果下标,也就是最接近tmp的endTime
            while(l <= r){
                int mid = (l + r) / 2;
                if(jobs[mid][0] > tmp){//这里是不合法情况 去左侧区间找
                    r = mid - 1;
                }else{
                    index = mid;//合法区间是这里所以我们用index来记录我们的合法小标 这里很关键,我们用一个index来存储合法小标就避免最后分析到底是l还是r或者l-1,r+1
                    l = mid + 1;
                }
            }
            
            if(index != -1){//如果不为-1,那么显然是找到了1个index下标 进行更新 这这里index+1是因为我们的dp数组是从1开始的
                dp[i + 1] = max(dp[i], dp[index + 1] + jobs[i][2]);
            }else{//没找到就是 自身的利润和dp[i]比大小 取最大值
                dp[i + 1] = max(dp[i], jobs[i][2]);
            }
            cout << index << ' ' << dp[i + 1] << endl;
            
        }
        return dp[n];
    }
};
  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值