【富途编程题】做项目的最大收益问题(贪心)、最长无重复子串(滑动窗口)

题目

做项目的最大收益问题

给定两个整数W和K,W代表你拥有的初始资金,K代表你最多可以做K个项目。再给定两个长度为N的正数数组costs[]和profits[],代表一共有N个项目,costs[i]和profits[i]分别表示第i号项目的启动资金与做完后的利润(注意是利润,如果一个项目的启动资金为10,利润为4,代表该项目最终的收入为14)。你不能并行只能串行地做项目,并且手里拥有的资金大于或等于某个项目的启动资金时,你才能做这个项目。该如何选择做项目,能让你最终的收益最大?返回最后能获得的最大资金。
[要求] 时间复杂度为 O(klogn),空间复杂度为 O(n)。
和原题不一样的是,这里返回值是 long 类型。

  • 输入与输出

第一行三个整数N, W, K。表示总的项目数量,初始资金,最多可以做的项目数量 第二行有N个正整数,表示costs数组
第三行有N个正整数,表示profits数组。
输入:
4 3 2
5 4 1 2
3 5 3 2
输出:
11
解释:
初始资金为3,最多做两个项目,每个项目的启动资金与利润见costs和profits。最优选择为:先做2号项目,做完之后资金增长到6。然后做1号项目,做完之后资金增长到11。其他的任何选择都不会比这种选择好,所以返回11。

最长无重复数组

给定一个数组arr,返回arr的最长无重复元素子数组的长度,无重复指的是所有数字都不相同。
子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组

  • 输入与输出

输入:
[2,3,4,5]
输出:
4
解释:
[2,3,4,5]是最长子数组

思路

做项目的最大收益问题

  1. 建立节点,记录每个项目的 cost 和 profits;
  2. 生成比较器,小根堆保存每个节点,大根堆根据情况(K 的取值、当前资金)保存值;
  3. 判断当前资金是否满足条件;
  4. 返回结果。

最长无重复子串

暴力解法
  1. 双层循环遍历数组,如果有重复的,就打破循环,判断长度。
滑动窗口
  1. 定义 Map,key = arr[i] ,value = arr[i] 的下标;
  2. 遍历数组,如果当前出现过,那么更新左窗口的最大值;
  3. 每次循环更新长度;
  4. 返回结果。

代码

做项目的最大收益问题

import java.util.Comparator;
import java.util.PriorityQueue;

public class Slution{
    /**
     * return the max money
     * @param costs int整型一维数组 the costs
     * @param profits int整型一维数组 the profits
     * @param W int整型 the money you have first
     * @param K int整型 max projects
     * @return long长整型
     */
    public static long maxMoney (int[] costs, int[] profits, int W, int K) {
        // write code here

        // 返回结果是 long 如果不定义 long res 的话结果可能会错误
        long res = W;

        // 将项目的花费与收益存在 nodes 数组中
        Node[] nodes = new Node[profits.length];
        for (int i = 0; i < profits.length; i++) {
            nodes[i] = new Node(profits[i], costs[i]);
        }

        PriorityQueue<Node> minQueue = new PriorityQueue<>(new MinHeapCom());
        PriorityQueue<Node> maxQueue = new PriorityQueue<>(new MaxHeapCom());

        // 建立小根堆, 按照 cost(花费) 排序
        for (int i = 0; i < profits.length; i++) {
            minQueue.add(nodes[i]);
        }

        // K 代表最多可做项目数量, 因此有了这个循环
        for (int i = 0; i < K; i++) {

            // 当 minQueue 不为空 并且 花费小于 W(初始资金) 时, 大根堆添加节点
            while (!minQueue.isEmpty() && minQueue.peek().cost <= res) {
                maxQueue.add(minQueue.poll());
            }

            // 如果资金不够做下一个项目, 那么返回收益 res
            if (maxQueue.isEmpty()) {
                return res;
            }

            // 如果够, 那么 res += 当前项目的收益
            res += maxQueue.poll().profit;
        }
        return res;
    }

    // 小根堆
    static class MinHeapCom implements Comparator<Node> {
        @Override
        public int compare(Node o1, Node o2) {
            return o1.cost - o2.cost;
        }
    }

    // 大根堆
    static class MaxHeapCom implements Comparator<Node> {
        @Override
        public int compare(Node o1, Node o2) {
            return o2.profit - o1.profit;
        }
    }


    // 定义节点
    static class Node {
        int profit;
        int cost;

        public Node(int profit, int cost) {
            this.profit = profit;
            this.cost = cost;
        }
    }
}

最长无重复子串

暴力
    public int maxLength (int[] arr) {
        // write code here

        // 定义结果
        int res = -1;
        for (int i = 0; i < arr.length; i++) {
            // 每次使用 set 保存
            Set<Integer> set = new HashSet<>();
            // 添加当前元素
            set.add(arr[i]);
            for (int j = i+1; j < arr.length; j++) {
                // 如果添加失败, 那么说明有重复的元素,打破循环
                if (!set.add(arr[j])) {
                    break;
                }
            }
            // 比较当前长度与原来记录的长度
            res = Math.max(res,set.size());
        }
        return res;
    }
滑动窗口
    public static int maxLength (int[] arr) {
        // 如果 arr.length < 2 直接返回结果
        if (arr.length < 2) {
            return arr.length;
        }

        // 定义结果
        int res = -1;

        // 使用 map 保存信息
        // key 代表当前元素, value 代表当前元素的下标
        Map<Integer, Integer> map = new HashMap<>();

        // 遍历数组
        for (int i = 0, begin = 0; i < arr.length; i++) {
            // 如果 map 中含有当前元素, 说明重复了
            if (map.containsKey(arr[i])) {
                // 此时需要判断左边界与原来的左边界的值, 如果
                begin = Math.max(map.get(arr[i]+1), begin);
            }
            res = Math.max(i - begin+1, res);
            map.put(arr[i],i);
        }
        return res;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值