贪心问题之初探--经典贪心问题

题目一
一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度为20的 金条,不管切成长度多大的两半,都要花费20个铜板。一群人想整分整块金 条,怎么分最省铜板?
例如,给定数组{10,20,30},代表一共三个人,整块金条长度为10+20+30=60. 金条要分成10,20,30三个部分。 如果, 先把长度60的金条分成10和50,花费60 再把长度50的金条分成20和30,花费50 一共花费110铜板。但是如果, 先把长度60的金条分成30和30,花费60 再把长度30金条分成10和20,花费30 一共花费90铜板。输入一个数组,返回分割的最小代价。

思想:经典的贪心算法,用小根堆来做:
首先根据给定的数组构造小根堆,每次poll出两个数据,然后将两数据之和相加后放回小根堆,整个过程中所有非叶子节点之和就是所求的最小代价,这也是一个标准的哈夫曼编码问题。

public class LessMoney {
    public static class MinHeapComparator implements Comparator<Integer>{
        public int compare(Integer o1,Integer o2){
            return o1-o2;
        }
    }
    public static int lessMoney(int[] arr){
        PriorityQueue<Integer> minHeap=new PriorityQueue<>(new MinHeapComparator());
        for(int i:arr){
            minHeap.add(i);
        }
        int sum=0;
        while(minHeap.size()>1){
            int add=minHeap.poll()+minHeap.poll();
            sum+=add;
            minHeap.add(add);
        }
        return sum;
    }
    public static void main(String[] args){
        int[] arr={10,20,30};
        System.out.println(lessMoney(arr));
    }
}
  • 题目二
    假设 力扣(LeetCode)即将开始其 IPO。为了以更高的价格将股票卖给风险投资公司,力扣 希望在 IPO 之前开展一些项目以增加其资本。 由于资源有限,它只能在 IPO 之前完成最多 k 个不同的项目。帮助 力扣 设计完成最多 k 个不同项目后得到最大总资本的方式。
    给定若干个项目。对于每个项目 i,它都有一个纯利润 Pi,并且需要最小的资本 Ci 来启动相应的项目。最初,你有 W 资本。当你完成一个项目时,你将获得纯利润,且利润将被添加到你的总资本中。
    总而言之,从给定项目中选择最多 k 个不同项目的列表,以最大化最终资本,并输出最终可获得的最多资本。
    示例 1:
    输入: k=2, W=0, Profits=[1,2,3], Capital=[0,1,1].
    输出: 4
    解释:
    由于你的初始资本为 0,你尽可以从 0 号项目开始。在完成后,你将获得 1 的利润,你的总资本将变为 1。此时你可以选择开始 1 号或 2 号项目。由于你最多可以选择两个项目,所以你需要完成 2 号项目以获得最大的资本。因此,输出最后最大化的资本,为 0 + 1 + 3 = 4。

    注意:
    假设所有输入数字都是非负整数。
    表示利润和资本的数组的长度不超过 50000。
    答案保证在 32 位有符号整数范围内。
    算法思想:
    根据项目cost来构建小根堆,如果初始资本>cost,则依次弹出项目,根据profit构建大根堆,每次要做的项目从大根堆拿出来;直到项目数目满足条件或者大根堆为空,终止,返回最后的w.
public class IPO {
    public static class Node{
        private int cost;
        private int profit;
        public Node(int cost,int profit){
            this.cost=cost;
            this.profit=profit;
        }
    }
    public static class minCostComparator implements Comparator<Node>{
        public int compare(Node o1,Node o2){
            return o1.cost-o2.cost;
        }
    }
    public static class maxProfitComparator implements Comparator<Node>{
        public int compare(Node o1,Node o2){
            return o2.profit-o1.profit;
        }
    }
    public static int maxProfit(int[] cost,int[] profit,int w,int k){
        Node[] nodes=new Node[profit.length];
        PriorityQueue<Node> minCostQueue=new PriorityQueue<>(new minCostComparator());
        PriorityQueue<Node> maxProfitQueue=new PriorityQueue<>(new maxProfitComparator());
        for(int i=0;i<profit.length;i++){
            nodes[i]=new Node(cost[i],profit[i]); //构建项目节点数组
            minCostQueue.add(nodes[i]);//构建小根堆
        }
        for(int i=0;i<k;i++){
//如果当前项目可以做,依次弹出;构建大根堆
            while(!minCostQueue.isEmpty()&&minCostQueue.peek().cost<=w){
                maxProfitQueue.add(minCostQueue.poll());
            }
            if(maxProfitQueue.isEmpty()){ //当前获得的w总和不够开启下一次项目
                return w;
            }
            w+=maxProfitQueue.poll().profit;//做完一次项目,将profit累加
        }
        return w;
    }
}
  • 题目三
    一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目 的宣讲。 给你每一个项目开始的时间和结束的时间(给你一个数组,里面 是一个个具体的项目),你来安排宣讲的日程,要求会议室进行 的宣讲的场次最多。返回这个最多的宣讲场次。
    算法思想:
    这里关键是贪心策略的选择:
    ①哪个项目早,就开始哪个项目,很容易举出反例,不正确;
    ②哪个项目持续时间短就开始哪个项目,也可以举出反例,不正确;
    ③哪个项目结束时间短,就开始哪个项目(正确的贪心策略)
public class BeatArranges {
    public static class Programs{
        private int start;
        private int end;
        public Programs(int start,int end){
            this.start=start;
            this.end=end;
        }
    }
    public static class EarliestEndComparator implements Comparator<Programs>{
        public int compare(Programs o1,Programs o2){
            return o1.end-o2.end;//这里的贪心策略按照哪个项目结束时间早,就做哪个项目
        }
    }
    public static int MaxPrograms(Programs[] programs,int currentTime){
        Arrays.sort(programs,new EarliestEndComparator());
        int count=0;
        for(int i=0;i<programs.length;i++){
            if(currentTime<=programs[i].start){
                count++;
                currentTime=programs[i].end;
            }
        }
        return count;
    }
    public static void main(String[] args){
        
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

浅唱战无双

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

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

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

打赏作者

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

抵扣说明:

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

余额充值