算法与数据结构-贪心算法及背包问题解决

序言

五大常用算法包括:回溯法 + 贪心算法 + 动态规划 + 分治法 + 分支限界法

之前已经介绍过回溯法及其在八皇后问题的应用。本文介绍贪心算法及其常见应用场景。

贪心算法

  • 贪心算法的基本原理
问题求解时,不是从全局最优解来考虑,而是通过求解局部最优解来产生全局最优解或全局最优解的近似解。

概念要点:
    1. 局部最优解
    2. 无后效性,即不可回溯
  • 贪心算法的特性:所求解的问题一般具有两个特性

    • [1] 贪心选择性质:整体最优可以通过一系列局部最优的选择来达到。

      • 贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
    • [2] 最优子结构性质:一个问题的最优解已经包含了其子问题的最优解。

      • 因此可以依次解决其子问题得到原问题的解
  • 贪心算法的基本思路

    • 建立数学模型
    • 问题分成若干子问题
    • 子问题求解得到子问题的局部最优解(以贪心选择开始)
    • 子问题局部最优合成原问题的一个解
  • 贪心算法使用的要点

    • 问题是否可用贪心算法求解:经验判断,适当证明

      • 贪心算法适用范围窄,很多时候需要根据经验决定是否选择贪心算法
      • 通过数学归纳法进行证明,利用贪心选择性质和最优子结构性质
    • 贪心标准的选择
      • 很重要,标准选择不好很可能导致解题失败
      • 在选择贪心标准时,我们要对所选的贪心标准进行验证才能使用,不要被表面上看似正确的贪心标准所迷惑
  • 与动态规划问题的区别

    • 相似:均通过递推实现,均具有最优子结构性质,均无后效性
    • 区别:
      • 贪心算法每一步的最优解一定包含上一步最优解。上一步最优解不做保留
      • 动态规划全局最优一定包含某个局部最优,但不一定包含前一个局部最优。需要记录之前所有最优解
  • 贪心算法常见应用场景

      1. 公共资源争用的活动安排
        • 教室不同时间段活动安排
      1. 背包问题
        • 不是”0-1背包问题“
      1. 最优装载问题
        • 与背包问题类似
      1. 钱币找零问题
        • 最少纸币张数支付K元

贪心算法应用举例

以背包问题为例简单举例

  • 问题描述

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

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

  • 贪心标准

    • [1] 先把价值高的装进去:这样重量消耗太快,不利于总体价值的增长
    • [2] 先把重量小的装进去:这样能保证重量有效增长,但价值不能有效增长
    • [3] 先把价值重量比最大的装进去:按物品权重排序,实现重量和总价值的有效增长
  • 代码(C)

#include <stdio.h>
#define true 1
#define false 0

typedef struct pack_node
{
    int weight;
    int value;
    float value2weight;     //价值重量比
    char flag;
}packNode;

int main()
{
    int Weight[7] = {35, 30, 60, 50, 40, 15, 20};
    int Value[7] = {10, 40, 30, 50, 35, 40, 30};
    packNode back_pack[7];
    int i = 0;
    for (; i < 7; i++)
    {
        back_pack[i].weight = Weight[i];
        back_pack[i].value = Value[i];
        back_pack[i].flag = false;
        back_pack[i].value2weight = (float)((float)(Value[i]) / (float)Weight[i]);
    }
    int allWeight = 0;
    int allValue = 0;
    float max = 0.0;        //value / weight最大比值
    char markArr[7] = {0};
    int j, eleFlag;
    float portion;
    while (allWeight < 150)
    {
        j = 0;
        max = back_pack[j].value2weight;
        while (j < 7)
        {
            if (back_pack[j].value2weight > max && back_pack[j].flag == false)
            {
                max = back_pack[j].value2weight;
                eleFlag = j;
            }
            j++;
        }
        back_pack[eleFlag].flag = true;
        allWeight += back_pack[eleFlag].weight;
        allValue += back_pack[eleFlag].value;
        markArr[eleFlag] = 1;
        if (allWeight > 150)
        {
            portion = (back_pack[eleFlag].weight - (allWeight - 150)) / (float)back_pack[eleFlag].weight;
            printf("第%d个物品选取:%.1f\n", eleFlag + 1, portion);
        }
    }
    int k = 0;
    printf("取出物品标号如下:");
    for (; k < 7; k++)
        printf("%d%c",markArr[k], k == 6 ? '\n' : ' ');
    return 0;
}

Acknowledgements:

http://blog.csdn.net/liufeng_king/article/details/8709005

http://blog.csdn.net/liufeng_king/article/details/8711928

http://blog.csdn.net/qfikh/article/details/51959226

http://blog.csdn.net/qq_23100787/article/details/50490658

2017.09.14

    原文作者:贪心算法
    原文地址: https://blog.csdn.net/baidu_35692628/article/details/77918793
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞