4fb2bd03d79b3e93e2a505157f5b0813
贪心算法

1 概念

  贪心的意思在于在作出选择时,每次都要选择对自身最为有利的结果,保证自身利益的最大化。贪心算法就是利用这种贪心思想而得出一种算法。

  贪心算法作为五大算法之一,在数据结构中的应用十分广泛。例如:在求最小生成树的Prim算法中,挑选的顶点是候选边中权值最小的边的一个端点。在Kruskal算法中,每次选取权值最小的边加入集合。在构造霍夫曼树的过程中也是每次选择最小权值的节点构造二叉树。这种每次在执行子问题的求解时,总是选择当前最优的情形,恰好符合贪心的含义。

  贪心算法可以简单描述为:大事化小,小事化了。对于一个较大的问题,通过找到与子问题的重叠,把复杂的问题划分为多个小问题。并且对于每个子问题的解进行选择,找出最优值,进行处理,再找出最优值,再处理。也就是说贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望得到结果是最好或最优的算法。

  贪心算法在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

2 算法流程

  (1)建立数学模型来描述问题。
  (2)把求解的问题分成若干个子问题。
  (3)对每一子问题求解,得到子问题的局部最优解。
  (4)把子问题的局部最优解合成原来问题的一个解。

3 伪代码

从问题的某一初始解出发
    while (能朝给定总目标前进一步) 
        do
            选择当前最优解作为可行解的一个解元素;
    由所有解元素组合成问题的一个可行解。

4 货币选择问题

问题描述:
  小明手中有1,5,10,50,100五种面额的纸币,每种纸币对应张数分别为5,2,2,3,5张。若小明需要支付456元,则需要多少张纸币?

问题分析:
(1)建立数学模型
  设小明每次选择纸币面额为Xi,需要的纸币张数为n张,剩余待支付金额为V,则有:
X1+X2+...+Xn = 456.
(2)问题拆分为子问题
  小明选择纸币进行支付的过程,可以划分为n个子问题:即每个子问题对应为:
在未超过456的前提下,在剩余的纸币中选择一张纸币。
(3)制定贪心策略,求解子问题
  制定的贪心策略为:在允许的条件下选择面额最大的纸币。则整个求解过程如下:

  • 选取面值为100的纸币,则X1 = 100, V = 456 - 100 = 356;
  • 继续选取面值为100的纸币,则X2 = 100, V = 356 - 100 = 256;
  • 继续选取面值为100的纸币,则X3 = 100, V = 256 - 100 = 156;
  • 继续选取面值为100的纸币,则X4 = 100, V = 156 - 100 = 56;
  • 选取面值为50的纸币,则X5 = 50, V = 56 - 50 = 6;
  • 选取面值为5的纸币,则X6 = 5, V = 6 - 5 = 1;
  • 选取面值为1的纸币,则X7 = 1, V = 1 - 1 = 0;求解结束

(4)将所有解元素合并为原问题的解
  小明需要支付的纸币张数为7张,其中面值100元的4张,50元1张,5元1张,1元1张。

代码实现:

#include<iostream>
#include<algorithm>
using namespace std;
const int N=5; 
int Count[N]={5,2,2,3,5};//每一张纸币的数量 
int Value[N]={1,5,10,50,100};//每一张的面额  
int solve(int money) 
{
    int num=0;
    for(int i=N-1;i>=0;i--) 
    {
        int c=min(money/Value[i],Count[i]);//每一个所需要的张数 
        money=money-c*Value[i];
        num+=c;//总张数 
    }
    if(money>0) num=-1;
    return num;
}
int main() 
{
    int money;
    cin>>money;
    int res=solve(money);
    if(res!=-1) cout<<res<<endl;
    else cout<<"NO"<<endl;
}

5 汽车加油问题

问题描述:
  一辆汽车加满油后可行驶 n公里。旅途中有若干个加油站。设计一个有效算法,指出应 在哪些加油站停靠加油,使沿途加油次数最少。

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

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

  输入样例:
  7 7
  1 2 3 4 5 1 6 6
  输出样例:
  4
问题分析:
  汽车加油问题可以分解为若干子问题,每个子问题对应为:是否在下一个加油站进行加加油。根据子问题设计贪心选择策略为:在行驶到下一个加油站之前,判断当前所剩的油量是否能支撑汽车到达下一个加油站,如果能,那么汽车不加油,反之,汽车加油。

代码实现:

```

include

using namespace std;
void Times(int n, int k, int l[])
{
int flag=1; // 设置flag判断汽车是否可以到达目的地
int total=0; // 表示汽车行驶过路程
int times=0; // 表示加油的次数

top Created with Sketch.