1235. 规划兼职工作

问题描述:

你打算利用空闲时间来做兼职工作赚些零花钱。
这里有 n 份兼职工作,每份工作预计从 startTime[i] 开始到 endTime[i]结束,报酬为 profit[i]。

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

注意,时间上出现重叠的 2 份工作不能同时进行。
如果你选择的工作在时间 X结束,那么你可以立刻进行在时间 X 开始的下一份工作。

在这里插入图片描述

问题思路:

方法一:动态规划

按照endTime进去排序,然后维护dp数组,保存接到第几份任务时能获得的最大报酬。
dp[i]: 当选到第i份工作时,可以拿到的最大报酬
dp[i]=max{dp[j]}+profit, i>j,且startTime[j]>=endTime[i]
用一个Job对象来存储startTime、endTime和profile

class Solution {


    class Job implements Comparable<Job>{

        private int startTime;
        private int endTime;
        private int profit;
        public Job(int startTime, int endTime, int profit){
            this.startTime = startTime;
            this.endTime = endTime;
            this.profit = profit;
        }

         public int compareTo(Job job){
            return Integer.compare(this.endTime, job.endTime);
        }

    }
       public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {

       

        // 先按结束时间排序
        Job[] jobs = new Job[startTime.length];

        for (int i = 0; i < startTime.length; i++) {
            Job job = new Job(startTime[i],endTime[i],profit[i]);
            jobs[i] = job;
        }
        
       Arrays.sort(jobs);

        // dp存放最大报酬
        int[] dp= new int[startTime.length];
        int total = 0;
        int max = 0;
        
        for (int i = 0; i < jobs.length; i++) {
            max=0;
            // dp[i] = max{dp[j]}+profit[i], 其中i>j,startTime[i] >= endTime[j]
            for (int j = 0; j < i; j++) {
                if(jobs[i].startTime >= jobs[j].endTime){
                    max = Math.max(max,dp[j]);
                }
            }
            dp[i] = max + jobs[i].profit;
            total = Math.max(total, dp[i]);
        }
        return total;
    }
}

方法二:动态规划+二分

动态规划是每次遍历到dp[i],都需要查询前面所有的dp[j],然后寻找最大的那个。
可是原数组已经按右端点排序了,即按结束时间排序,那我们直接找到第一个小于dp[i]开始时间的区间,即dp[j].endTime < dp[i].startTime.
然后插入它后面,比较薪酬取最大值插入即可。

class Solution {


    class Job implements Comparable<Job>{

        private int startTime;
        private int endTime;
        private int profit;
        public Job(int startTime, int endTime, int profit){
            this.startTime = startTime;
            this.endTime = endTime;
            this.profit = profit;
        }

         public int compareTo(Job job){
            return Integer.compare(this.endTime, job.endTime);
        }

    }
   public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
        // 先按结束时间排序
        Job[] jobs = new Job[startTime.length];
        for (int i = 0; i < startTime.length; i++) {
            Job job = new Job(startTime[i],endTime[i],profit[i]);
            jobs[i] = job;
        }
        Arrays.sort(jobs);
         // dp存放最大报酬
        int[] dp= new int[startTime.length];
        dp[0] = jobs[0].profit;
        // 最大报酬
        int total = jobs[0].profit;
        

        for (int i = 1; i < jobs.length; i++) {
            // dp[i] = max{dp[j]}+profit[i], 其中i>j,startTime[i] >= endTime[j]
            // 直接找到当前的最晚结束时间,从它开始去对比薪酬
            // 返回前面i-1个元素中,不和i个区间时间重叠的起始区间
            int k = findMaxLat(jobs, i-1,jobs[i].startTime);

            // 如果该区间与前面的所有区间重叠
            if(k<0){
                dp[i] = Math.max(dp[i-1],jobs[i].profit);
            }else{
                dp[i] = Math.max(dp[i-1],dp[k]+jobs[i].profit);
            }
            total = Math.max(total, dp[i]);
        }
        return total;

    }

    // 找到最后一个小于K的数
    public int findMaxLat(Job[] jobs, int right, int k){

        int left = -1;
        int mid;

        while (left<right){
            mid = (left+right+1)/2;
            if(jobs[mid].endTime <= k){
                left = mid;
            }else{
                right = mid - 1;
            }
        }
        return left;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值