【每日一题Day4】LC1235.规划兼职工作

简介: 常规的线性dp只是单纯的依赖dp[i-1],复杂度由状态的维度数确定

序列dp


  • 常规的线性dp只是单纯的依赖dp[i-1],复杂度由状态的维度数确定


  • 而序列dp是要根据规则来找前驱,复杂度由状态的维度数和找前驱的复杂度共同决定


规划兼职工作【LC1235】


你打算利用空闲时间来做兼职工作赚些零花钱。


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


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


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


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


2022/10/22 小垃圾只能写写暴力,第一次看到序列dp的概念,看来还是做的不够多


动态规划+二分查找


参考宫水三叶姐的题解


定义三元组job[i]=(startTime[i],endTime[i],profit[i])


1.确定dp数组(dp table)以及下标的含义


dp[i]:前i份兼职工作可以获得的最大报酬


2.确定递推公式


。如果不选择该工作


dp[i] = dp[i-1];


。如果选择该工作


  • 仅选择完成该工作:dp[i] = job[i-1][2]


  • 将该工作接在某个工作后面完成:需要在所有满足**job[j][1] <= job[i-1][0]**中选择报酬最大的dp[j+1]


dp[i] = dp[j+1] + job[i-1][2]


dp[i] = Math.max(dp[i-1],dp[j]+job[i-1][2])


3.如何初始化


。dp[0]=0;


。当我们处理到 job[i]时,为了能够「将所有所能拼接在 job[i]前面的 job[j]归结到一边」并且「所能更新 dp[i]的 dp[j]均被计算」,需要对所有的 job[i]进行右端点(结束时间)进行排升序,并按照从小到大的方式处理每个 job[i]。


4.确定遍历顺序


由dp公式可知,按照结束时间从小到大的顺序遍历job


5.举例推导dp数组


  • 代码


class Solution {
    public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
        int len = startTime.length;
        int[][] data = new int[len][3];
        for (int i = 0; i < len;i++){
            data[i] = new int[]{startTime[i],endTime[i],profit[i]};
        }
        // 根据endTime进行从小到大排序
        Arrays.sort(data,new Comparator<int []>(){
            public int compare(int[] data1, int[] data2){
                return data1[1] - data2[1];
            }
        });
        int[] dp = new int[len+1];
        dp[0] = 0;
        for (int i = 1; i <= len; i++){
            // 二分 左闭右开 查找结束时间小于等于当前工作开始时间的最大下标
            int left = 0;
            int right = i-1;
            int target = data[i-1][0];
            while (left < right){
                int mid = left + (right-left)/2;
                if (data[mid][1] > target){
                    right = mid;
                }else{
                    left = mid + 1;
                }
            }
            dp[i] = Math.max(dp[i-1],dp[left] + data[i-1][2] );
        }
        return dp[len];
    }
}


  • 复杂度


。时间复杂度:O(nlogn);dp有n个状态需要转移,每次转移需要进行二分


。空间复杂度:O ( n )


动态规划+下标排序


将下标按照结束时间从小到大,开始时间从小到大排序,然后遍历下标,dp数组定义同上


class Solution {
    public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
        int len = profit.length;
        int[] dp = new int[len];
        Integer[] idx = new Integer[len];
        Arrays.parallelSetAll(idx, Integer::valueOf);
        Arrays.sort(idx, (e1, e2) -> {
            if (endTime[e1] == endTime[e2]) {
                return startTime[e1] - startTime[e2];
            }
            return endTime[e1] - endTime[e2];
        });
        dp[0] = profit[idx[0]];
        for (int i = 1; i < len; i++) {
            dp[i] = profit[idx[i]];
            for (int j = i - 1; j >= 0; j--) {
                // 逆序遍历 找到第一个结束时间小于等于当前开始时间的 即为dp[j]+profit[idx[i]]即为考虑当前工作的最大报酬
                if (endTime[idx[j]] <= startTime[idx[i]]) {
                    dp[i] += dp[j];
                    break;
                }
            }
            dp[i] = Math.max(dp[i], dp[i - 1]);
        }
        return dp[len - 1];
    }
}


  • 复杂度


。时间复杂度:O ( n 2 )


。空间复杂度:O ( n )


回溯[超出时间限制]


class Solution {
    int maxProfit = 0;
    public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
        // boolean[] isWorked = new boolean[startTime.length];// 记录该工作是否兼职过 减少重复计算
        int len = startTime.length;
        int[][] data = new int[len][3];
        for (int i = 0; i < len;i++){
            data[i] = new int[]{startTime[i],endTime[i],profit[i]};
        }
        // 根据startTime进行从小到大排序
        Arrays.sort(data,new Comparator<int []>(){
            public int compare(int[] data1, int[] data2){
                return data1[0] - data2[0];
            }
        });
        // 排序
        backtracking(-1,0,data);
        return maxProfit;
    }
    public void backtracking(int preIndex,int pro,int[][] data){
        int preEndTime = preIndex == -1 ? 0 : data[preIndex][1];
        for (int i = preIndex + 1; i < data.length; i++){
            if (data[i][0] >= preEndTime){
                pro += data[i][2];
                maxProfit = Math.max(maxProfit,pro);
                backtracking(i,pro,data);
                pro -= data[i][2];
            }
        }
    }
}


目录
相关文章
|
3月前
|
安全 容灾 项目管理
 2023年上半年信息系统项目管理师综合知识真题与答案解释(1)
 2023年上半年信息系统项目管理师综合知识真题与答案解释(1)
125 1
|
3月前
|
监控 数据挖掘 测试技术
2023年上半年信息系统项目管理师___综合知识真题与答案解释(2)
2023年上半年信息系统项目管理师___综合知识真题与答案解释(2)
53 3
2023年上半年信息系统项目管理师___综合知识真题与答案解释(2)
|
3月前
|
安全 网络安全 测试技术
【题目】2023年国赛信息安全管理与评估正式赛任务书-模块3 CTF
【题目】2023年国赛信息安全管理与评估正式赛任务书-模块3 CTF
【题目】2023年国赛信息安全管理与评估正式赛任务书-模块3 CTF
|
3月前
|
安全 网络协议 网络安全
【题目】2023年国赛信息安全管理与评估正式赛任务书-模块3 理论技能
【题目】2023年国赛信息安全管理与评估正式赛任务书-模块3 理论技能
|
8月前
|
算法 测试技术 C#
C++二分查找算法:规划兼职工作
C++二分查找算法:规划兼职工作
|
11月前
|
设计模式 算法 Dubbo
网传8月虾皮规模毁offer,程序员该如何做未来的规划和技术储备?
前言 正文开始之前,咱们先了解一下Shopee究竟是一家什么样的公司?给的薪资如何? Shopee(东南亚及中国台湾地区的电商平台)
|
C语言
头歌c语言实训项目-综合案例课外练习:大奖赛现场统分
头歌c语言实训项目-综合案例课外练习:大奖赛现场统分
头歌c语言实训项目-综合案例课外练习:大奖赛现场统分
LeetCode每日一题——1235. 规划兼职工作
你打算利用空闲时间来做兼职工作赚些零花钱。 这里有 n 份兼职工作,每份工作预计从 startTime[i] 开始到 endTime[i] 结束,报酬为 profit[i]。
123 0
LeetCode每日一题——1235. 规划兼职工作
|
算法
每日一题冲刺大厂提高组 第二十四天 跑路
大家好,我是泡泡,给大家带来每日一题的目的是为了更好的练习算法,我们的每日一题为了让大家练到各种各样的题目,熟悉各种题型,一年以后,蜕变成为一个不一样的自己!
98 0
|
机器学习/深度学习 算法 机器人
Interview:算法岗位面试—11.07早上上海某机器人公司(上市)面试之项目考察、比赛考察、图像算法的考察等
Interview:算法岗位面试—11.07早上上海某机器人公司(上市)面试之项目考察、比赛考察、图像算法的考察等