zoukankan      html  css  js  c++  java
  • 贪心算法和动态规划

    贪心算法:在求解问题时,总是做出在当前看来做好的选择,所以它是局部最优解,试图通过局部最优推出全局最优。需要注意的是贪心算法没有固定的算法框架,算法设计的关键是选择贪心策略,其必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。

    常见的用贪心算法解决的问题,比如教师排课系统,任务调度。

    eg:现在有一个场地,明天有n个活动,给出n个活动的开始和结束时间,如何安排最多场次的活动?

    针对上面的问题,我们要选出一种贪心策略,比如选择越早开始的活动优先级高,或者越早结束的活动优先级高,或者占用时间最短的活动优先级高,没有固定的框架,要更多的进行穷举找到相对合适的贪心策略。

    现在我们认为,越早开始,活动次数就越多:

    public class ActivityGreedy {
        public static void main(String[] args) {
            List<Activity> activityList = Lists.newArrayList(
                    new Activity(1, 10, 12),
                    new Activity(2, 9, 10),
                    new Activity(3, 8, 9),
                    new Activity(4, 12, 14),
                    new Activity(5, 11, 13),
                    new Activity(6, 14, 15),
                    new Activity(7, 13, 14)
            );
            // 根据开始时间排序
            activityList = activityList.stream().sorted(Comparator.comparing(Activity::getStart)).collect(Collectors.toList());
            int cur = 0;
            for (Activity activity : activityList) {
                // 当前活动的开始时间比上场活动的结束时间靠后,认为可以进行当前活动
                if (activity.getStart() >= cur) {
                    System.out.println(activity.toString());
                    // 时间指针指向当前活动的结束时间
                    cur = activity.getEnd();
                }
            }
        }
    }
    
    @Getter
    @ToString
    class Activity {
        public int num;// 活动编号
        public int start;// 活动开始时间
        public int end; // 活动结束时间
        public Activity(int num, int start, int end) {
            this.num = num;
            this.start = start;
            this.end = end;
        }
    }

    输出结果:

    Activity(num=3, start=8, end=9)
    Activity(num=2, start=9, end=10)
    Activity(num=1, start=10, end=12)
    Activity(num=4, start=12, end=14)
    Activity(num=6, start=14, end=15)

    这就是以我们选择的贪心策略,最终给我们计算的最优解。 

    上面的问题,用贪心算法可以处理,但是有些情况,我们一定要拿到最优解,这样就不能使用贪心算法了,这时候就要用到动态规划的算法思想。

    动态规划的基本思想:问题可以分解成子问题,子问题可以重复,针对子问题求最优解,最终得到原问题的最优解。

    背包问题:一个背包,容量50kg,三个物品,分别是10kg/30元,20kg/50元,40kg/60元,物品不能分割,且每件物品只能拿一件,如何获取最大价值?

    动态规划求解的核心在于找到状态转移方程:

    把5kg的袋子,按照1kg累加计算

      10kg 20kg 30kg 40kg 50kg
    加物品1 30  30 30 30 30
    加物品2 30 50 30+50=80 80  80
    加物品3 30 50 30+50=80 80 30+60=90

    上面表示的是:在当前重量下,加入某商品时,产生的最大价值。能装这个物品的时候,和上面的价值比较,大的话就装,小就不装。

    定义金额数组value[],重量数组weight[],商品遍历下标i,能获取到状态转移方程:

    Math.max(value[i] + res[i][j - weight[i]], res[i-1][j]);

    公式翻译成代码:

    public static void main(String[] args) {
            // 重量
            int w = 50;
            // 商品数量
            int n = 3;
            // 商品价值
            int[] value = {30, 50, 60};
            // 商品重量
            int[] weight = {10, 20, 40};
            // 计算结果,使用二维数组保存当前重量下装到当前物品时的最大价值
            int[][] res = new int[3 + 1][w + 1];
            // 两层循环,第一层是每次要增加的物品
            for (int i = 1; i <= n; i++) {
                // 第二层表示每次要背包分割的重量
                for (int j = 1; j <= 50; j++) {
                    // 当前重量装的下当前物品
                    if (j >= weight[i - 1]) {
                        // 要判断,当前加入这个物品,和不加这个物品,在同等重量下哪个价值更高
                        res[i][j] = Math.max(value[i - 1] + res[i - 1][j - weight[i - 1]], res[i - 1][j]);
                    }
                    // 当前重量装不下当前物品,最大重量和没装这个物品时一样
                    else {
                        res[i][j] = res[i - 1][j];
                    }
                }
            }
            System.out.println(res[3][w]);
        }

    输出结果:90

    再看一个示例,比如购物车有n件商品,现在中奖了,可清除购物车,额度10000元,如何使现有商品清除总额最高?

     public static void main(String[] args) {
            // 最高清除总额10000元
            int max = 10000;
            List<Item> itemList = Lists.newArrayList(
                    new Item("手机", 2999),
                    new Item("耳机", 1099),
                    new Item("鼠标", 211),
                    new Item("键盘", 632),
                    new Item("充电器", 256),
                    new Item("空调", 1788),
                    new Item("净化器", 2102),
                    new Item("净水器", 2099),
                    new Item("电风扇", 666),
                    new Item("马桶", 1492),
                    new Item("冰箱", 1673),
                    new Item("电脑", 3727)
            );
            // 商品价格列表
            List<Integer> value = itemList.stream().map(Item::getValue).collect(Collectors.toList());
            // 存储拿到某个商品时在某个总额下能清除的最大商品总额
            int[][] res = new int[value.size() + 1][max + 1];
            // 第一层遍历所有商品
            for (int i = 1; i <= value.size(); i++) {
                // 可清除总额分割
                for (int j = 1; j <= max; j++) {
                    if (j >= value.get(i - 1)) {
                        res[i][j] = Math.max(value.get(i - 1) + res[i - 1][j - value.get(i - 1)], res[i - 1][j]);
                    } else {
                        res[i][j] = res[i - 1][j];
                    }
                }
            }
            // 输出最大总额
            System.out.println(res[value.size()][max]);
            // 输出具体选了哪些商品:逻辑是,最后一个商品和倒数第二个商品,在10000额度的时候如果前者总额大于后者总额,说明最后一个品可清除,以此类推
            int temp = max;
            for (int i = value.size(); i >= 1; i--) {
                if (res[i][temp] > res[i - 1][temp]) {
                    System.out.println(itemList.get(i - 1).toString());
                    temp -= itemList.get(i - 1).getValue();
                }
            }
        }
    
    
    @Getter
    @ToString
    class Item {
        private String name;
        private int    value;
    
        public Item(String name, int value) {
            this.name = name;
            this.value = value;
        }
    }

    输出结果:

    额度:9997
    Item(name=冰箱, value=1673)
    Item(name=马桶, value=1492)
    Item(name=净化器, value=2102)
    Item(name=键盘, value=632)
    Item(name=耳机, value=1099)
    Item(name=手机, value=2999)

    以上就是个人对贪心算法和动态规划的理解。

  • 相关阅读:
    [ZJOI2006]书架
    luogu P3369 【模板】普通平衡树(splay)
    MegaCli是一款管理维护硬件RAID软件,可以通过它来了解当前raid卡的所有信息,包括 raid卡的型号,raid的阵列类型,raid 上各磁盘状态
    ipmi配置方法-20200328
    debian配置---->/etc/apt/sources.list apt基本源设置指南
    Alien 魔法:RPM 和 DEB 互转
    Debian 9 中设置网络
    SSH自动断开连接的原因-20200323
    mpstat命令
    dstat命令
  • 原文地址:https://www.cnblogs.com/dlcode/p/14130205.html
Copyright © 2011-2022 走看看