动态规划-规划兼职工作

6 篇文章 0 订阅

动态规划-规划兼职工作

一、问题描述

你打算利用空闲时间来做兼职工作赚些零花钱。这里有 n 份兼职工作,每份工作预计从 startTime 开始到 endTime 结束,报酬为 profit。给你一份兼职工作表,包含开始时间 startTime,结束时间 endTime 和预计报酬 profit 三个数组,请你计算并返回可以获得的最大报酬。

注意

  • 时间上出现重叠的 2 份工作不能同时进行

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

二、问题分析

例如现在输入一组数据:

  • startTime = [1,2,3,3]

  • endTime = [3,4,5,6]

  • profit = [50,10,40,70]

表示兼职表有4份工作:

工作1:开始时间:1,结束时间:3,薪资:50

工作2:开始时间:2,结束时间:4,薪资:10

工作3:开始时间:3,结束时间:5,薪资:40

工作4:开始时间:3,结束时间:6,薪资:70

第一步:找出最优解的性质,并刻画其结构特征。

简单尝试穷举法:

方案1:工作1或工作2=50或10

方案2:工作1+工作3=50+40=90

方案3:工作1+工作4=50+70=120

发现问题:组合很多,由于有起始时间和结束时间导致没有很好的排序组合方案

在这里插入图片描述

三、动态规划方程,即递归关系

第二步:递归定义最优值

在这里插入图片描述

  • dp[i] 表示前i份兼职工作可以获得的最大报酬。
  • k 表示满足结束时间小于等于第i−1 份工作开始时间的兼职工作数量。
  • profit[i−1]表示第i份工作的薪酬。
  • 该公式表示:完成第i份兼职获得的最大报酬=MAX(考虑前一份(i-1)兼职的最大报酬,第i份兼职开始时间前能完成的兼职的最大报酬+第i份兼职的报酬)。

四、代码分析

第三步:自底向上的方式计算最值

1.基本代码和解释

public static int jobScheduling(int[] startTime, int[] endTime, int[] profit, int[] dp, String[] optimal) {
    // 工作数量
    int n = startTime.length;
    // 存储工作的
    int[][] jobs = new int[n][];
    // 放入
    for (int i = 0; i < n; i++) {
        jobs[i] = new int[]{startTime[i], endTime[i], profit[i]};
    }
    // 按结束时间排序
    Arrays.sort(jobs, Comparator.comparingInt(a -> a[1]));
    // 对每份工作判断
    for (int i = 1; i <= n; i++) {
        // 查找合适的工作
        // k 表示满足结束时间小于等于第i−1份工作开始时间的兼职工作数量
        int k = binarySearch(jobs, i - 1, jobs[i - 1][0]);
        // dp[i]=max(dp[i−1],dp[k]+profit[i−1])
        // 每份工作薪资和(前一份工作薪资,当前工作开始时间前可以结束的工作薪资+当前工作薪资)
        dp[i] = Math.max(dp[i - 1], dp[k] + jobs[i - 1][2]);
        //判断是否选择了i兼职
        if (dp[i] == dp[i - 1]) {
            // 如果未选择,表示i-1前是最优解
            optimal[i] = optimal[i - 1];
        } else {
            // 如果选择表示:最优解=i开开始前最优解+i
            optimal[i] = (optimal[k] + " " + String.valueOf(i)).trim();
        }
    }
    return dp[n];
}
public static int binarySearch(int[][] jobs, int right, int target) {
        int left = 0;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (jobs[mid][1] > target) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

2.测试

public static void main(String[] args) {
    // 开始时间
    int[] startTime = {1, 2, 3, 3};
    // 结束时间
    int[] endTime = {3, 4, 5, 6};
    // 薪资表
    int[] profit = {50, 10, 40, 70};
    // 报酬数组
    int[] dp = new int[startTime.length + 1];
    // 最优解数组
    String[] optimal = new String[startTime.length + 1];
    optimal[0] = " ";
    int i = jobScheduling(startTime, endTime, profit, dp, optimal);
    System.out.println("共获得报酬=" + i);
    System.out.println("工作和薪酬关系=" + Arrays.toString(dp));
    System.out.println("最优兼职表=" + Arrays.toString(optimal));
}

共获得报酬=120
工作和薪酬关系=[0, 50, 50, 90, 120]
最优兼职表=[ , 1, 1, 1 3, 1 4]

问题总结

在这道动态规划案例中:

  • 要点

    完成第i份兼职获得的最大报酬=MAX(考虑前一份(i-1)兼职的最大报酬,第i份兼职开始时间前能完成的兼职的最大报酬+第i份兼职的报酬)。
    在计算时考虑当前兼职时,要用到之前子问题的解时,我们直接查兼职与最大薪资表dp就可以简化运算。

  • 算法性能分析

    • 时间复杂度:O(nlogn),其中 n 是兼职工作的数量。排序需要 O(nlogn),遍历 + 二分查找需要 O(nlogn),因此总时间复杂度为 O(nlogn)。
    • **空间复杂度:O(n)。**需要 O(n) 的空间来保存dp。
  • 现实意义

    通过学习动态规划,弄懂该案例,不光可以学习如何兼职获取最大收益,也能用在其他和时间有关的规划问题中,

    设计动态规划算法的步骤

    (1)找出最优解的性质,并刻画其结构特性。

    (2)递归地定义最优值。

    (3)以自底向上的方式计算最优值

    (4)根据计算最优值时得到的信息,构建最优解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

卑微小钟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值