取数组中n个数之和为不大于目标值的最大值

本文介绍了一种使用动态规划解决如何从已排序的任务列表中选取任务,以使总工时最接近预设目标的问题。通过递归划分决策树,并采用贪心策略选取最优路径,实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近写了一道题,来分享下。

题目

小明衡量自己的工作情况,自己能完成60个单位的工时,现有一组任务,已经按需要花费的工时降序排序,请从中选出n个任务,使得小明工作达到最饱和,输出小明今天需要工作几个工时

如:{20,19,15,13,10,9}

输出:58

说明:20 + 15 + 13 + 10 = 58 最接近60

思路

一开始想到的是贪心算法,因为给定的数组本身就是一个降序排序的正整数数组,只需要从头开始遍历并记录当前的和,如果当前和 + 当前工时 < 目标工时则将当前工时加入,但是这种方法我们最后得到的是20+19+15=54 只能获取到尽可能大的单个任务,无法达到总工时尽可能大。

后来试图穷举,但是如果穷举数组长度为n就需要n个循环来得到每一个解,难以实现。

最后,采用了动态规划来解这道题。

解说

我们可以将这道题划分为两部分——数组中第一个数据一定被选中,数组中第一个数据一定不被选中,在这两部分中取最大值,这两部分又分别可以划分为第二个数据一定被选中和第二个数据一定不被选中两部分取最大值。

公式

即将原问题定义为fn(20,19,15,13,10,9)可进行如下拆分

fn(20,19,15,13,10,9)=max(fn(19,15,13,10,9) + 20,fn(19,15,13,10,9))

图解

后面划分与之前相同,不再做细分。

代码

 

直接上代码

/**
 * @author 大地崩坏苍蝇兽
 * @date 2022/11/8 - 19:51
 */
public class Solution {

    public static void main(String[] args) {
        int[] array = { 20 , 19, 15, 13, 10, 9};
        System.out.println(getSumClosestToTarget(array, 0, TARGET));
    }

    final static int TARGET = 60;

    public static int getMaxNotMoreThanTarget(int a, int b, int target) {
        //如果两个数都大于目标值,返回0
        if (target < a && target < b) {
            return 0;
        }
        //如果一个数大于目标值,一个数小于目标值,返回小于目标值的的数
        if (target < a) {
            return b;
        }
        if (target < b) {
            return a;
        }
        //两个数都小于目标值返回较大值
        return a > b ? a : b;
    }

    /**
     * 返回数组从start下标开始到数组末尾之和
     *
     * @param array 数组
     * @param start 起始下标
     * @return 数组start下标到末尾之和
     */
    public static int getSum(int[] array, int start) {
        int sum = 0;
        for (int i = start; i < array.length; i++) {
            sum += array[i];
        }
        return sum;
    }

    /**
     * @param array  数组
     * @param start  起始下标
     * @param target 目标值
     * @return
     */
    public static int getSumClosestToTarget(int[] array, int start, int target) {
        //目标值小于0,不可能有这种情况,因为工时都是正整数
        if (target < 0) {
            return 0;
        }
        //如果当前数组全部之和小于目标值直接返回
        int sum = getSum(array, start);
        if (sum <= target) {
            return sum;
        }
        //取两部分较大值
        return getMaxNotMoreThanTarget(getSumClosestToTarget(array, start + 1, target - array[start]) + array[start]
                , getSumClosestToTarget(array, start + 1, target)
                , target);
    }
}

看看运行结果

得出答案,欸嘿

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值