• 贪心算法基本思想和典型例题(转)


    贪心算法

    一、算法思想

    贪心法的基本思路:
    ——从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。
    该算法存在问题:
    1. 不能保证求得的最后解是最佳的;
    2. 不能用来求最大或最小解问题;
    3. 只能求满足某些约束条件的可行解的范围。


    实现该算法的过程:
    从问题的某一初始解出发;
    while 能朝给定总目标前进一步 do
       求出可行解的一个解元素;
    由所有解元素组合成问题的一个可行解;

    二、例题分析

    1、[背包问题]有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
    要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。

    物品 A  B  C D E F G
    重量 35  30  60  50  40  10  25  
    价值  10  40  30 50  35  40  30


    分析:

    目标函数: ∑pi最大
    约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150)


    (1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
    (2)每次挑选所占空间最小的物品装入是否能得到最优解?
    (3)每次选取单位容量价值最大的物品,成为解本题的策略。

    http://baike.baidu.com/view/298415.htm

     

        实现这个算法是学习算法分析与设计这门课程的需要。

        贪心算法是所接触到的第一类算法。算法从局部的最优出发,简单而快捷。对于一个问题的最

    优解只能用穷举法得到时,用贪心法是寻找问题次优解的较好算法。

        贪心法是一种改进了的分级处理方法。用贪心法设计算法的特点是一步一步地进行,根据某个

    优化测度(可能是目标函数,也可能不是目标函数),每一步上都要保证能获得局部最优解。每一

    步只考虑一个数据,它的选取应满足局部优化条件。若下一个数据与部分最优解连在一起不再是可

    行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加为止。这种能够

    得到某种度量意义下的最优解的分级处理方法称为贪心法。

        选择能产生问题最优解的最优度量标准是使用贪心法的核心问题。

        假定有n个物体和一个背包,物体i 有质量wi,价值为pi,而背包的载荷能力为M。若将物体i的

    一部分xi(1<=i<=n,0<=xi<=1)装入背包中,则有价值pi*xi。在约束条件

    (w1*x1+w2*x2+…………+wn*xn)<=M下使目标(p1*x1+p2*x2+……+pn*xn)达到极大,此处

    0<=xi<=1,pi>0,1<=i<=n.这个问题称为背包问题(Knapsack problem)。

        要想得到最优解,就要在效益增长和背包容量消耗两者之间寻找平衡。也就是说,总应该把那

    些单位效益最高的物体先放入背包。

        在实现算法的程序中,实现算法的核心程序倒没碰到很大的问题,然而实现寻找最优度量标准

    程序时麻烦不断!

        在寻找最优度量标准时,大致方向是用冒泡排序算法。也就是根据p[i]/w[i]的大小来对w[i]来

    排序。

        在直接用此算法时,可以有如下的一段代码:

        //根据效益tempArray[i]对重量w[i]排序,为进入贪心算法作准备

    1   void sort(float tempArray[], flaot w[], int n)

    2   {

    3       int i = 0, j = 0;

    4       int index = 0;

    5    

    6       //用类似冒泡排序算法,根据效益p[i]/w[i]对w[i]排序

    7       for (i = 0; i < n; i++)

    8    {

    9           float swapMemory = 0;

    10          float temp;

    11

    12          temp = tempArray[i];

    13          index = i;

    14

    15          for (j = i + 1; j < n; j++)

    16          {          

    17              if (temp < tempArray[j])

    18        {

    19                  temp = tempArray[j];

    20                  index = j;

    21        }

    22       }

    23   

    24          //对w[i]排序

    25          swapMemory = w[index];

    26          w[index] = w[i];

    27          w[i] = swapMemory;

    28      }

    29

    30      return;

    31  }

        然而仔细对算法分析后可以发现,“拿来主义”在这里用不上了!

        对算法的测试用例是p[3] = {25, 24, 15};w[3] = {18, 15, 10}。得到的结果如下:

        please input the total count of object: 3

        Please input array of p :

        25 24 15

        Now please input array of w :

        18 15 10

        sortResult[i] is :

        1   -107374176.000000   1   1.600000   2    1.600000

        after arithmetic data: x[i]

        0.000000        0.333333        0.000000 

        可以看到其效益为x[3] = {1.4, 1.6, 1.5},于是在M = 20的情况下,其预想中的输出结果是

    0,1,0.5。然而事实上是不是就这样呢?

        当程序进入此函数经过必要的变量初始化后,进入了外围循环,也就是程序的第7行。第一轮循

    环中,temp = tempArray[0] = 1.4,index = i = 0;程序运行到第15行,也就是进入了内层循环。

    内层循环的主要任务是从第i + 1个元素之后找到一个最大的效益并保存此时的下标。到了第24行后

    ,就开始对w[i]进行排序。

        问题就在这里了!排序后的w[i] = {1.6, 1.6, 1.5},因此对w[i]排序后就既改变了w[i]的原

    有顺序,还改变了w[i]的原来值!

        据此,做出一些修改,得到了如下的一段代码:

    1   void sort(float tempArray[], int sortResult[], int n)

    2   {

    3       int i = 0, j = 0;

    4       int index = 0, k = 0;

    5

    6       for (i = 0; i < n; i++)//对映射数组赋初值0

    7    {

    8           sortResult[i] = 0;

    9    }

    10

    11      for (i = 0; i < n; i++)

    12      {

    13          float swapMemory = 0;

    14          float temp;

    15

    16          temp = tempArray[i];

    17          index = i;

    18

    19          for (j = i; j < n; j++)

    20          {          

    21              if ((temp < tempArray[j]) && (sortResult[j] == 0))

    22        {

    23                  temp = tempArray[j];

    24                  index = j;

    25        }

    26       }

    27

    28          if (sortResult[index] == 0)

    29       {

    30              sortResult[index] = ++k;

    31       }

    32      }

    33

    34      for (i = 0; i < n; i++)

    35      {

    36          if (sortResult[i] == 0)

    37       {

    38              sortResult[i] = ++k;

    39       }

    40      }

    41

    42      return;

    43  }

        修改后最大的一个改变是没有继续沿用直接对w[i]排序,而是用w[i]的一个映射数组

    sortResult[i]。sortResult[i]中元素值存放的是根据效益计算得w[i]的大小顺序!这样w[i]原有

    的值和位置都没有改变,从而使算法得以实现!

        至于有没有更好的实现版本,还在探索中!

    #include <stdio.h>

    #define MAXSIZE 100  //假设物体总数

    #define M 20      //背包的载荷能力

    //算法核心,贪心算法

    void GREEDY(float w[], float x[], int sortResult[], int n)

    {

        float cu = M;

        int i = 0;

        int temp = 0;

        for (i = 0; i < n; i++)//准备输出结果

        {

            x[i] = 0;

        }

        for (i = 0; i < n; i++)

        {

            temp = sortResult[i];//得到取物体的顺序

            if (w[temp] > cu)  

            {

                break;

            }

            x[temp] = 1;//若合适则取出

            cu -= w[temp];//将容量相应的改变

        }

        if (i <= n)//使背包充满

        {

            x[temp] = cu / w[temp];

        }

        return;

    }

    void sort(float tempArray[], int sortResult[], int n)

    {

        int i = 0, j = 0;

        int index = 0, k = 0;

        for (i = 0; i < n; i++)//对映射数组赋初值0

        {

            sortResult[i] = 0;

        }

        for (i = 0; i < n; i++)

        {

            float temp = tempArray[i];

            index = i;

            //找到最大的效益并保存此时的下标

            for (j = 0; j < n; j++)

            {          

                if ((temp < tempArray[j]) && (sortResult[j] == 0))

                {

                    temp = tempArray[j];

                    index = j;

                }

            }

            //对w[i]作标记排序

            if (sortResult[index] == 0)

            {

                sortResult[index] = ++k;

            }

        }

        //修改效益最低的sortResult[i]标记

        for (i = 0; i < n; i++)

        {

            if (sortResult[i] == 0)

            {

                sortResult[i] = ++k;

            }

        }

        return;

    }

    //得到本算法的所有输入信息

    void getData(float p[], float w[], int *n)

    {

        int i = 0;

        printf("please input the total count of object: ");

        scanf("%d", n);

        printf("Please input array of p : ");

        for (i = 0; i < (*n); i++)

        {

            scanf("%f", &p[i]);

        }

        printf("Now please input array of w : ");

        for (i = 0; i < (*n); i++)

        {

            scanf("%f", &w[i]);

        }

        return;

    }

    void output(float x[], int n)

    {

        int i;

        printf(" after arithmetic data: advise method ");

        for (i = 0; i < n; i++)

        {

            printf("x[%d] ", i);

        }

        printf(" ");

        for (i = 0; i < n; i++)

        {

            printf("%2.3f ", x[i]);

        }

        return;

    }

    void main()

    {

        float p[MAXSIZE], w[MAXSIZE], x[MAXSIZE];

        int i = 0, n = 0;

        int sortResult[MAXSIZE];

        getData(p, w, &n);

        for (i = 0; i < n; i++)

        {

            x[i] = p[i] / w[i];

        }

        sort(x, sortResult, n);

        GREEDY(w, x, sortResult, n);

        output(x, n);

        getch();

    }

  • 相关阅读:
    树链剖分 关于点权与边权的转换
    2018 CCPC 吉林站 H Lovers || HDU 6562 (线段树哦)
    统计学习方法(一)概念
    python学习心得(三)
    python学习心得
    Python学习:基本概念
    Python学习(一)
    SparkMLlib聚类学习之KMeans聚类
    SparkMLlib回归算法之决策树
    SparkMLlib学习之线性回归
  • 原文地址:https://www.cnblogs.com/great-xxr/p/5677495.html
Copyright © 2020-2023  润新知