算法设计与分析——贪心算法

文章目录

  • 1. 贪心算法
    • 1.1 定义
    • 1.2 基本要素
    • 1.3 基本思想
    • 1.4 特点
  • 2. 典型案例
    • 2.1 背包问题(物品可分割)
      • 2.1.1 问题描述
      • 2.1.2 问题转换
      • 2.1.3 问题求解步骤
    • 2.2 背包问题与0-1背包问题的区别
    • 2.3 贪心算法可以求解背包问题,那么能不能用贪心算法求解0-1背包问题?若不能,为什么?去反例说明。
    • 2.4 最优装载问题
      • 2.4.1 问题描述
      • 2.4.1 问题转化
      • 2.4.2 贪心选择策略
      • 2.4.2 代码实现

1. 贪心算法

1.1 定义

在求解最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最终解,这种求解方法就是贪心算法。

贪心算法所做的选择可以依赖于以往所做过的选择,但绝不依赖于将来的选择,也不依赖于子问题的解。

1.2 基本要素

(1)贪心选择性质:所求问题的整体最优解可以通过一系列局部最优的选择(即贪心选择)来达到。

(2)最优子结构性质:一个问题的最优解包含其子问题的最优解时。

1.3 基本思想

在对问题求解时,总是做出在当前看来是最好的选择。

1.4 特点

(1)不是从整体考虑——得到的解可能不是全局最优

(2)简单,直接,易理解,效率高。

2. 典型案例

2.1 背包问题(物品可分割)

2.1.1 问题描述

给定一个载重量为M的背包,考虑n个物品,其中第i个物品的重量wi ,价值vi(1≤in),要求把物品装满背包,且使背包内的物品价值最大。

2.1.2 问题转换

其中 wi 为第 i 个物品的重量,xi 取值为1 或 0,表示装入和不装入背包。

2.1.3 问题求解步骤

(1)首先计算每种物品单位重量的价值 vi / wi ,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。

(2)若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包。

(3)依此策略一直地进行下去,直到背包装满为止。

2.2 背包问题与0-1背包问题的区别

有两类背包问题(根据物品是否可以分割),如果物品不可以分割,称为0—1背包问题(动态规划);如果物品可以分割,则称为背包问题(贪心算法)。

  • 0-1背包问题只有放入和不放入两种情况,且物品不能拆分。

  • 背包问题的物品可以进行拆分。

  • 完全背包问题和0-1背包问题基本一致,但完全背包问题种每个物品可以选择多次。

2.3 贪心算法可以求解背包问题,那么能不能用贪心算法求解0-1背包问题?若不能,为什么?去反例说明。

不能。使用贪心算法不能得到最优解。

主要原因是:由于0-1背包问题无法将物品分割,贪心算法就无法保证最终能将背包装满,部分闲置的背包空间使每公斤背包空间的价值降低了。

举例说明:背包容量为50,n个物品如下

n 1 2 3
重量 10 20 30
价值 60 100 120
性价比 6 5 4

按照贪心算法的思想,背包容量为50,我们将物品1装入,然后将物品2装入,对物品3进行判断时,由于仅剩20的容量,重量为30的物品3没法装入,因此此时得到的价值为160,剩余20容量的空间闲置;而使用动态规划时,可以得到的最优解为220。

2.4 最优装载问题

2.4.1 问题描述

有一批集装箱要装上一艘载重量为c的轮船,其中集装箱 i 的重量为 wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。

输入样例 输出样例
50 3 40 10 40 37 5 10 30 24 35 40 25 3 30 40 50 2 1 2 2 1 3 No answer!

2.4.1 问题转化

image-20210609100519385

xi 取值为1或0,表示装入和不装入轮船。

2.4.2 贪心选择策略

按照集装箱重量最轻的先装入轮船。

2.4.2 代码实现

/*
    @author cc
    @date 2021/4/19
    @Time 10:59
    To change this template use File | Settings | File Templates.
    最优装载问题
*/
#include
#include "string.h"
#include "algorithm"

using namespace std;

// 定义集装箱的数据结构
struct load{
    int index;      // 集装箱的编号
    int weight;     // 集装箱的重量
};

load box[1001];         // 集装箱类型的数组,存储集装箱
int x[1001];            // 用于记录集装箱是否被装入,数组的下标即为集装箱的编号

// 自定义排序因子,按照重量从小到大排序
bool cmp(load a, load b){
    if(a.weight < b.weight) return true;
    return false;
}

int main(){
    int c, n;
    cout << "请输入集轮船的载重量c和集装箱的个数n(空格分开):" << endl;
    while (cin >> c >> n){       // C/C++中定义EOF为-1  该行语句等同于C中的: while(scanf("%d,%d",&c,&n)!=EOF){
        memset(box,0, sizeof(box));      // 将数组box的每个值均初始化为0
        memset(x,0, sizeof(x));         // 将数组x的每个值均初始化为0
        for (int i = 1; i <= n; ++i) {      // i++是先使用i,再让i加1;而++i是先让i加1,再使用i。
                                            // 之所以在for循环中没有影响,是因为我们并没有在i自增的前后使用i,而是执行循环体之后才使用i
            cin >> box[i].weight;
            box[i].index = i;
        }
        stable_sort(box,box+n+1,cmp);       // 该排序函数与sort区别在于,当有对个相同的元素时,stable_sort不会改变其原有的顺序
        if(box[1].weight>c){    // 如果第一个集装箱的重量就大于c则后面的均无法装入
            cout << "No answer!";
            continue;
        }
        // 贪心算法的实现,重量最轻者优先装载
        int i;
        for (i = 1; i <= n && box[i].weight <= c ; i++) {
            x[box[i].index] = 1;
            c -= box[i].weight;		// 每装入一个,c同时减少
        }
        // 输出装载的集装箱数量
        cout << i-1 << endl;
        // 输出装载集装箱的编号
        for (int j = 1; j <= n; ++j) {
            if(x[j]) cout << j << " ";
        }
        cout <

你可能感兴趣的:(算法设计与分析,算法,贪心算法,数据结构)