贪心算法&策略实现

贪心算法

贪心算法:基于某种情况进行一个排序。 贪心算法得到的是优良解,而非全局最优解。需要证明局部最优解 == 全局最优解

经典贪心算法 —— 会议问题

对于这个问题 ,我们提出贪心策略:

策略1:按照会议的持续时间长短来排序。持续时间短的会议优先安排

我们可以举出反例:蓝色方案安排的场次比绿色方案安排的场次少

策略2:按照会议的开始时间早晚来排序。开始时间早的会议优先安排

我们可以举出反例:蓝色方案安排的场次比绿色方案安排的场次少

.......

策略n:按照会议的结束时间早晚来排序。结束时间早的会议优先安排

这个策略是正确的,先上代码

package greedyalgorithms;

import java.util.Arrays;
import java.util.Comparator;

public class ScheduleProgram {
    class Progame {//会议
        int start;
        int end;

        public Progame(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }

    public static class ProgameComparator implements Comparator<Progame> {
        //按结束时间从早到晚排序
        @Override
        public int compare(Progame o1, Progame o2) {
            return o1.end - o2.end;//o1的结束时间比o2的结束时间晚,o2排前面
            //返回-1(或负数),表示不需要交换o1和o2的位置,o1排在o2前面
            //返回1(或正数),表示需要交换o1和o2的位置,o2排在o1前面
        }
    }

    //progames:需要安排的会议,timePoint:目前的时间点
    public int bestArrange(Progame[] progames, int timePoint) {
        Arrays.sort(progames, new ProgameComparator());//按照ProgameComparator比较器定义的compare()方法来排序
        int result = 0;
        
        for (int i = 0; i < progames.length; i++) {
            if(timePoint <= progames[i].start){//此时时间 <= 会议的开始时间
                result++;//安排好的会议的个数++
                timePoint = progames[i].end;//时间来到会议的结束时间
            }
        }
        
        return result;
    }
}

对于这个策略,我们不能够轻松举出反例,但证明需要使用严格复杂的数学证明

贪心算法在笔试时的解题套路

1、准备暴力枚举、全排列的模板,贪心算法的最优解一定在全排列之中

2、贪心策略类题目,是一句某个标准排序或是放在堆里各自排序,举出n多个比较策略。此时容易举出反例的比较策略可以直接pass

3、用策略X结合随机大样本跑对数器。如果某个策略几千万次的随机样本都相同,那么这个比较策略就是正确的

什么是对数器?对数器的作用是什么?-CSDN博客

贪心策略实现

切分金条的最小分割代价

哈夫曼编码问题

从反方向考虑,已经切好的数组如何合并使得花费最小,花费的金额为合并的数值

贪心策略:每次挑代价最小的2个数来结合

如果有一组数组:arr = {2, 3, 4, 7, 9, 2}

将数组放入小根堆中arr = {2, 2, 3, 4, 7, 9}
前两个数相加arr = {4, 3, 4, 7, 9}
排序arr = {3, 4, 4, 7, 9}
前两个数相加arr = {7, 4, 7, 9}
排序arr = {4, 7, 7, 9}
前两个数相加arr = {11, 7, 9}
排序arr = {7, 9, 11}
前两个数相加arr = {16, 11}
排序arr = {16, 11}
两个数相加arr = {27}
package greedyalgorithms;

import java.util.PriorityQueue;

public class LessMoneySplitGold {
    public static int lessMoney(int[] arr) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();//默认小根堆
        for (int i = 0; i < arr.length; i++) {
            priorityQueue.add(arr[i]);//将数组全部放入小根堆
        }

        int temp = 0;
        while (priorityQueue.size() > 1) {
            temp = priorityQueue.poll() + priorityQueue.poll();//弹出两个相加
            priorityQueue.add(temp);//加回去,排序
        }

        return priorityQueue.poll();//最后小根堆中只有一个数即为结果
    }
}

做项目获得的最大收益

costs[]:花费数组,profits[]: 利润数组

这里的花费仅仅表示做项目的门槛,因为在实际题目的计算中并没有减去项目花费的数

实现解析

一组项目:<花费,利润>

<2,3>,  <1,4>,  <1,1>,  <2,7>.,  <3,2>,  <4,10>

将项目放入一个以花费为依据的小根堆中,其中,利润的大小不考虑

小根堆中的项目表示都是锁住(不可做)的状态

小根堆中:<1,4>,  <1,1>,   <2,3>,  <2,7>.,  <3,2>,  <4,10>

依据初始资金m,解锁所有花费小于等于m的项目放到一个以利润为依据的大根堆中,其中,花费的大小不考虑

大根堆中的项目表示解锁(可做)的状态

m = 1; 大根堆中:<1,4>,  <1,1>

大根堆抛出第一个项目,初始资金更新为m+该项目的利润

依据新的初始资金m,在小根堆中解锁项目,在大根堆中抛出,更新利润 .......

直到到达指定的项目数k停止

此外,还有另一种情况。当没有达到指定的项目数k,但某一时刻的初始资金比较少,不能够解锁小根堆中剩下的所有项目,此时只能提前返回当前的资金m

package greedyalgorithms;

import java.util.*;

public class FindMaxmizedCapital {
    int M = 0;//初始资金
    int K = 0;//表示最多做k个项目

    class Program {
        public int cost;//花费
        public int profit;//利润

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

    public int findMaxmizedCapital(List<Program> programs, int M, int K) {
        PriorityQueue<Program> minCostPQ = new PriorityQueue(new MinCostComparator());
        PriorityQueue<Program> maxProfitPQ = new PriorityQueue(new MaxProfitComparator());
        //全部放入小根堆中锁定
        for (Program pr : programs) {
            minCostPQ.add(pr);
        }

        for (int i = 0; i < K; i++) {
            //peek(): 获取元素但不删除队列首元素
            while (!minCostPQ.isEmpty() && minCostPQ.peek().cost <= M) {
                maxProfitPQ.add(minCostPQ.poll());//放入大根堆中,解锁
            }
            //更新初始资金
            M += maxProfitPQ.poll().profit;
            //当没有达到指定的项目数k,但某一时刻的初始资金比较少,不能够解锁小根堆中剩下的所有项目,此时只能提前返回当前的资金m
            if (maxProfitPQ.isEmpty()) {
                break;
            }
        }
        return M;
    }


    class MinCostComparator implements Comparator<Program> {
        @Override
        public int compare(Program o1, Program o2) {
            return o1.cost - o2.cost;
            //compare()方法比较o1,o2的大小
            //正序:当o1<o2时return -1,o1=o2时return 0,o1>o2时return 1

        }
    }

    class MaxProfitComparator implements Comparator<Program> {
        @Override
        public int compare(Program o1, Program o2) {
            return o2.profit - o1.profit;
        }
    }


}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
贪心算法是一种解决问题的策略,通常用于组合优化问题。在Excel中实现贪心算法需要遵循以下步骤: 1. 确定问题:确定问题的目标和限制条件。 2. 确定贪心策略:根据问题的性质,确定贪心策略。 3. 构建模型:将问题转换为Excel模型。 4. 编写函数:根据贪心策略编写Excel函数。 5. 执行贪心算法:在Excel中执行贪心算法。 以下是一个简单的贪心算法示例,它可以帮助你理解如何在Excel中实现贪心算法。 问题: 有一组数字,需要从中选出一些数字,使得这些数字的和最大。选出的数字必须满足两个限制条件: 1. 选出的数字的数量不能超过5个。 2. 选出的数字不能重复。 贪心策略: 按照数字的大小顺序,从大到小选择数字,直到选出的数字满足限制条件为止。 模型: 在Excel中,可以将数字列表存储在一个列或行中。假设数字列表存储在A列中,需要选出的数字存储在B列中。 函数: 可以使用Excel的排序函数(SORT)和索引函数(INDEX)来实现贪心算法。首先,使用SORT函数将数字列表按照从大到小的顺序排序。然后,使用INDEX函数在排序后的列表中选择前5个数字。 =SORT(A1:A10,1,FALSE) =INDEX(B1:B5,1) 执行贪心算法: 将函数放入单元格中,按下Enter键即可执行贪心算法。根据贪心策略,函数将选择前5个数字,并将它们存储在B列中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值