【算法基础】----贪心算法

贪心算法是常见的基础算法,它在求解问题时总想用当前看来最好的方法去实现,而到了下一步,再用下一步时最好的方法来解决,因此有了贪心的名字。此方法不从整体去考虑,仅是在某种意义上的局部最优求解。虽然贪心算法不是对所有的问题都能得到整体最优解,但是面对范围相当广泛的很多问题时,能产生整体最优解或是整体最优解的高度近似解,因此可见贪心算法只是追去一定范围内的最优。

1. 贪心算法的基础



贪心算法在解决一个问题时,通常会从某一个初始解出发,逐步逼近给定的目标,以便尽快求出更好的解。

当达到算法中的某一步不能在继续进行下去的时候,就停止算法,给出一个近似解。由贪心算法的特点和思路可以看出,贪心算法存在以下3个问题。

(1).不保证最后得解是最优的(实际上它从不考虑最后得结果)

(2).不能用来求最大或最小解的问题

(3).只能满足某些约束条件的可行解的范围

而通常情况下,贪心算法的思路如下:

(1).建立数学模型来描述问题

(2).将求解的问题分解为若干范围较小的子问题

(3).对每一子问题求解,得到子问题的局部最优解

(4).把子问题的局部最优合并成原问题的局部最优解

下面我们通过两个例子来描述贪心算法


1. 找零方案

在现实生活中,我们买东西经常需要找零,而商店找零的方法通常是优先找面额大的,再找小面额的。因为我们永远都不是商店的最后一个顾客,因此商店为了保证下一个顾客依然有零钱可以找,会尽可能的把面额大的找给我们,而把零钱留给自己。

人民币有100、50、20、10、5、1、0.5、0.2、0.1(单位:元)等多种面额,设计一个找零算法,使得面值大的尽可能多的被找。

#include <iostream>
using namespace std ;

int main(void) {
        int i ;
        float money ;
        float x[9] = {100 , 50 , 20 , 10 , 5 , 1 , 0.5 , 0.2 , 0.1} ;
        cin >> money ;
        for(i = 0 ; i < 9 ; ++ i) {
                while(money >= x[i]) {
                        money -= x[i] ;
                        cout << x[i] << "  " ;
                }
        }
        return 0 ;
}





上面只是一个简单的例子,下面我们来详细的讨论一下装箱问题

假设有编号为0,1,2.....n-1的n中物品,体积分别为v0,v1,v2......v(n-1),现需要将这些物品装进容积为V的若干箱子中,且箱子的体积不会小于任一物品。不同的装法可能。需要数量不同的箱子,现在我们需要用尽可能少的箱子装下所有的物品。

例:箱子的容量为10,有四个物品,体积分别为6,7,4,3,如果按照6->7->4->3的顺序,需要3个箱子,而如果按照6->4->7->3的顺序,只需要两个箱子。

选择数据结构

数据结构的选择关系到算法的高效与否。

存储链式结构,不涉及元素的进出,排除了栈和队列。有数组和链表可供选择,而我们在装箱问题中选择链表的原因有一下两点:

1.建立数据结构时,我们事先并不知道需要多少箱子,即无法确定元素的个数。

2.在算法的运行中,我们一旦确定了物品装进哪个箱子,就需要将物品和箱子建立某中内存上的联系,使得通过箱子可以找到其中的物品。

而数组显然不具备以上功能,因此我们选择链表。

typedef struct gNode {                  //structs array
        int num ;
        int gV ;
}gNode ;

typedef struct ElemNode {               //物品节点
        int gV ;
        int gNum ;
        struct ElemNode * next ;        //节点中指向下一个物品的指针
}ElemNode ;

typedef struct gBox {                   //箱子节点
        int V ;                         //箱子的容积
        ElemNode * nextNode ;           //箱子节点中指向其中物品的指针
        struct gBox * nextBox ;         //箱子节点中指向下一个箱子的节点
}gBox ;

最终程序运行时的数据结构应该是这样


实际代码如下:


#include <stdio.h>
#include <stdlib.h>

const int maxV = 10 ;

typedef struct gNode {                  //structs array
        int num ;
        int gV ;
}gNode ;

typedef struct ElemNode {               //物品节点
        int gV ;
        int gNum ;
        struct ElemNode * next ;        //节点中指向下一个物品的指针
}ElemNode ;

typedef struct gBox {                   //箱子节点
        int V ;                         //箱子的容积
        ElemNode * nextNode ;           //箱子节点中指向其中物品的指针
        struct gBox * nextBox ;         //箱子节点中指向下一个箱子的节点
}gBox ;

gBox * Pack(gBox * hB , gNode * hG , int num) {
        int i ;
        gBox * t , * iBox , * newBox;
        ElemNode * newNode = NULL , * findEnd = NULL ;
        hB = t = (gBox *)malloc(sizeof(gBox)) ;                 //拿出第一个物品,必定打开一个箱子
        t -> V = maxV ;                                         //
        t -> nextNode = NULL ;                                  //初始化箱子
        t -> nextBox = NULL ;                                   //
        for(i = 0 ; i < num ; ++ i) {      
                newNode = (ElemNode *)malloc(sizeof(ElemNode)) ;        //每一个物品,都需要分配一块内存
                newNode ->gV = (hG+i)->gV ;
                newNode ->gNum = (hG+i)->num ;
                newNode ->next = NULL ;//从第一个物品开始装,等到所有的物品都遍历完,说明装箱结束。
                for(iBox = hB ; iBox ; iBox = iBox ->nextBox) {
                        
                        //每拿到一个物品,都要从第一个箱子开始遍历,直到找到一个箱子或者现有的箱子都没法装下
                        if((hG + i) -> gV < iBox ->V) {         //如果当前箱子的剩余容积大于物品的体积,可以放
                                findEnd = iBox ->nextNode ;                             //使用findEnd指针来找到物品链的末尾
                                while(findEnd ->next || findEnd) {                      //使用或运算符可以兼容箱子中有物品或没物品
                                        findEnd = findEnd -> next ;                     //两种情况
                                }
                                findEnd ->next = newNode ;
                                iBox ->V -= newNode ->gV ;
                        }
                        
                }
                newBox = (gBox *)malloc(sizeof(gBox)) ;                 //如果当前所有箱子都不符合条件,创建一个新的箱子节点
                newBox ->V = maxV ;                                     //并初始化
                newBox ->nextNode = NULL ;                                          
                newBox ->nextBox = NULL ;
                t = t ->nextBox = newBox ;              //然后将新的箱子挂在箱子链的末尾
                t ->nextNode = newNode ;                //最后将新的物品挂在新的箱子上
        }
}

void Display(gBox * hBox) {
        int i = 1 ;
        ElemNode * travelNode = NULL ;
        gBox * travelBox = NULL ;
        for(travelBox = hBox ; travelBox ; travelBox = travelBox ->nextBox) {
                printf("第%d个箱子中装有物品:" , i ++) ;
                travelNode = travelBox ->nextNode ;
                while(travelNode) {
                        printf("%4d" , travelNode ->gNum) ;
                }
                printf("\n") ;
        }
}

int main(void) {
        int num , i ;
        printf("please input the number of boxes:") ;
        scanf("%d" , num) ;
        gNode * hG = (gNode *)malloc(num * sizeof(gNode)) ;             //创建结构体数组,用来存储物品信息
        for(i = 0 ; i < num ; ++ i) {
                (hG+i)->num = i ;
                printf("Input No.%d node:" , i) ;
                scanf("%d" , &(hG+i)->gV ) ;
        }
        gBox * hBox = NULL ;
        hBox = Pack(hBox , hG , num) ;
        Display(hBox) ;
        return 0 ;
}


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值