zoukankan      html  css  js  c++  java
  • 算法设计与分析:贪心算法

    贪心选择性:每一步贪心选出来的一定是原问题的最优解的一部分
    最优子结构:每一步贪心选完后会留下子问题,子问题的最优解和贪心选出来的解可以凑成原问题的最优解

    好文推荐:
    https://www.cnblogs.com/whsu/p/13906447.html
    https://blog.csdn.net/ZhifanSk/article/details/105217963

    磁带最优存储问题

    问题描述

    有n 个程序{1,2,…, n }要存放在长度为L的磁带上。程序i存放在磁带上的长度是Li, 1<= i<= n。这n 个程序的读取概率分别是p1,p2,…,pn,且pi+p2+…+pn = 1。如果将这n个程序按 i1,i2,…,in 的次序存放,则读取程序tr 所需的时间tr=c(Pi1Li2+Pi2Li2+…+PirLir)。这n 个程序的平均读取时间为t1+t2+…+tn。磁带最优存储问题要求确定这n个程序在磁带上的一个存储次序,使平均读取时间达到最小。

    由文件 input. txt给出输入数据。第1行是正整数n,表示文件个数。接下来的n行中,每行有2个正整数a和b,分别表示程序存放在磁带上的长度和读取概率。实际上第k个程序的读取概率为ak/∑ai,对所有输入均假定c=1。

    将计算的最小平均读取时间输出到文件 output. txt

    	输入文件示例				输出文件示例
    	5						85.6193	
    	71   872
    	46   452				 
        9   265
        73   120
        35   85		
    

    算法描述

    根据tr的计算公式,假设n个程序按 i1,i2,…,in 的次序存放,

    平均读取时间 = n*P1*L1+(n-1)*P2*L2+(n-2)*P3*L3+…+Pn*Ln

    由此可知要使平均读取时间最小,即需要使Pi*Li最小的程序放在最前面,它在计算式子中被算的次数最多。

    贪心求解过程:

    • 计算每个程序的长度和读取概率的乘积pi*li
    • 对序列按照pi*li升序排序
    • 求出n个程序的最小平均读取时间

    贪心选择性质:磁带问题有一个以贪心选择开始的最优解

    • 命题:磁带问题有一个最优解以贪心选择开始,即包含最小的l*p,即t0。
    • E数组是将磁带上的程序以l*p非递减排列得到的最优解,E = {t0,t1,t2……tn-1} 。

    证明:对于问题p=t0+t2+..+tn-1,因为问题的解包含了所有的tr,如果只存在一个最优解E,那么序列的顺序是特定的, 最优解E一定包含了t0,问题转换为证明最优解唯一。

    • 假设除E= {t0,t1,…ti……tj…tn-1} 之外还存在一个最优解E’= {t0,t1,…tj……ti…tn-1},那么访问的时间TE’<=TE,做差TE’-TE=(tj-ti)+(tj-ti)(j-i-1)+{(ti-tj)+(tj-ti)(j-i)}= (tj-ti)(2j-2*i)+1,由于tj>=ti,所以TE’>=TE
      ①若TE’=TE,二者等价于一个最优解
      ②若TE’>TE,与假设矛盾,所以说问题的最优解唯一
      因为只存在一个最优解E,那么序列的顺序是特定的,最优解E一定包含了t0
    • 结论:磁带问题有一个以贪心选择开始的最优解,即以t0开始

    关键代码

    		Scanner scanner = new Scanner(System.in);
            int N = scanner.nextInt();
            int[] l = new int[N];
            int[] p = new int[N];
            double[] w = new double[N];
            int sum = 0;
            for (int i = 0; i < N; i++) {
                l[i] = scanner.nextInt();
                p[i] = scanner.nextInt();
                sum += p[i];
            }
            for (int i = 0; i < N; i++) {
                w[i] = (double) p[i] * l[i] / sum;
            }
            Arrays.sort(w);
            double res = 0;
            for (int i = 0; i < N; i++) {
                res += (N - i) * w[i];
            }
            System.out.println(res);
    

    结果分析

    输入:
    5
    71 872
    46 452
    9 265
    73 120
    35 85

    输出:85.61928651059085

    输入:
    6
    71 800
    46 402
    19 365
    37 160
    53 95
    33 100

    输出:95.34027055150884

    算法时间复杂度:\(O(nlogn)\)

    n表示程序的个数,算法的主要时间花费在排序上

    算法空间复杂度:\(O(n)\)

    该贪心算法需要一个长度为n的数组存储每个程序的长度和读取概率的乘积pi*li

    汽车加油问题

    问题描述

    一辆汽车加满油后可行驶n公里。旅途中有若干个加油站。设计一个有效算法,指出应在哪些加油站停靠加油,使沿途加油次数最少。并证明算法能产生一个最优解。
    对于给定的n和k个加油站位置,计算最少加油次数。

    输入数据的第一行有2 个正整数n和k(n≤5000,k≤1000),表示汽车加满油后可行驶n公里,且旅途中有k个加油站。接下来的1 行中,有k+1 个整数,表示第k个加油站与第k-1 个加油站之间的距离。第0 个加油站表示出发地,汽车已加满油。第k+1 个加油站表示目的地。

    将计算出的最少加油次数输出。如果无法到达目的地,则输出“No Solution!”

    输入文件示例				输出文件示例
    7 7						4
    1 2 3 4 5 1 6 6
    

    算法描述

    最少加油次数,意味着每次加油都尽可能跑的远一些,直至跑不动了(跑不到下一个加油站),才在本加油站加油。这是一个贪心策略。

    贪心选择性质:设在加满油后可行驶的N千米这段路程上任取两个加油站A,B,且A相较于B距离起始点更近,则若在B加油不能到达终点,那么在A加油也一定不能到达终点。假设A与B的间距为x,则在B点加油可行驶的路程比在A点加油可行驶的路程要长x千米。所以根据贪心选择策略,为使得加油次数最少就会尽可能选择更远的加油站去加油。因此,加油次数最少满足贪心选择性质,该算法能产生一个最优解。

    关键代码

     		Scanner scanner = new Scanner(System.in);
            int n = scanner.nextInt();
            int k = scanner.nextInt();
            int[] dis = new int[k + 1];
            for (int i = 0; i < dis.length; i++) {
                dis[i] = scanner.nextInt();
                if (dis[i] > n) {
                    System.out.println("No Solution");
                    return;
                }
            }
            int res = 0;
            int oil = n;
            int i = 0;
    
            while (i < dis.length) {
                //i是目标
                if (oil - dis[i] < 0) {
                    //到第不了第i个加油站,需要立马加油
                    res++;
                    oil = n;
                    continue;
                }
                oil -= dis[i];
                i++;
            }
            System.out.println(res);
    

    结果分析

    输入:
    7 7
    2 3 6 6 6 6 6 6

    输出:6

    输入:
    7 7
    1 2 3 4 5 1 6 6

    输出:4

    输入:
    7 7
    7 7 7 7 7 7 7 7

    输出:7

    算法时间复杂度:\(O(n)\) ,n表示加油站的个数-1

    算法空间复杂度:\(O(1)\)

    友情,恋爱,推理,因为有当初的约定才努力不懈。
  • 相关阅读:
    CSS3 颜色渐变、阴影、渐变的阴影
    AxureRP分页签 / Tab选项卡切换功能~
    这是我的第一篇博客!
    天池nlp新人赛_task6
    天池nlp新人赛_task5
    天池nlp新人赛_task4
    天池nlp新人赛_task3.对TF-IDF进一步认识
    天池nlp新人赛_task2:数据预处理改进和一些思路
    天池nlp新人赛_task1
    希尔伯特空间
  • 原文地址:https://www.cnblogs.com/ziyanblog/p/15685273.html
Copyright © 2011-2022 走看看