zoukankan      html  css  js  c++  java
  • 贪心算法

    贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解

    贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

    思想

    贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止

    过程

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

    2. 把求解的问题分成若干个子问题;

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

    4. 把子问题的解局部最优解合成原来解问题的一个解。                            

    假设山洞中有 n 种宝物,每种宝物有一定重量 w 和相应的价值 v,毛驴运载能力有限,
    只能运走 m 重量的宝物,一种宝物只能拿一样,宝物可以分割。那么怎么才能使毛驴运走宝
    物的价值最大呢?
    我们可以尝试贪心策略:
    (1)每次挑选价值最大的宝物装入背包,得到的结果是否最优?
    (2)每次挑选重量最小的宝物装入,能否得到最优解?
    (3)每次选取单位重量价值最大的宝物,能否使价值最高?
    思考一下,如果选价值最大的宝物,但重量非常大,也是不行的,因为运载能力是有限
    的,所以第 1 种策略舍弃;如果选重量最小的物品装入,那么其价值不一定高,所以不能在
    总重限制的情况下保证价值最大,第 2 种策略舍弃;而第 3 种是每次选取单位重量价值最大
    的宝物,也就是说每次选择性价比(价值/重量)最高的宝物,如果可以达到运载重量 m,

    那么一定能得到价值最大。
    因此采用第 3 种贪心策略,每次从剩下的宝物中选择性价比最高的宝物。

    算法设计
    (1)数据结构及初始化。将 n 种宝物的重量和价值存储在结构体 three(包含重量、价
    值、性价比 3 个成员)中,同时求出每种宝物的性价比也存储在对应的结构体 three 中,将
    其按照性价比从高到低排序。采用 sum 来存储毛驴能够运走的最大价值,初始化为 0。
    (2)根据贪心策略,按照性价比从大到小选取宝物,直到达到毛驴的运载能力。每次选
    择性价比高的物品,判断是否小于 m(毛驴运载能力),如果小于 m,则放入,sum(已放入
    物品的价值)加上当前宝物的价值,m 减去放入宝物的重量;如果不小于 m,则取该宝物的
    一部分 m * p[i],m=0,程序结束。m 减少到 0,则 sum 得到最大值。

    完美图解
    假设现在有一批宝物,价值和重量如表 2-3 所示,毛驴运载能力 m=30,那么怎么装入
    最大价值的物品?
    表 2-3  宝物清单
    宝物 i  1  2  3  4  5  6  7  8  9  10
    重量 w[i]  4  2  9  5  5  8  5  4  5  5
    价值 v[i]  3  8  18  6  8  20  5  6  7  15
    (1)因为贪心策略是每次选择性价比(价值/重量)高的宝物,可以按照性价比降序排
    序,排序后如表 2-4 所示。
    表 2-4  排序后宝物清单
    宝物 i  2  10  6  3  5  8  9  4  7  1
    重量 w[i]  2  5  8  9  5  4  5  5  5  4
    价值 v[i]  8  15  20  18  8  6  7  6  5  3
    性价比 p[i]  4  3  2.5  2  1.6  1.5  1.4  1.2  1  0.75
    (2)按照贪心策略,每次选择性价比高的宝物放入:
    第 1 次选择宝物 2,剩余容量 30−2=28,目前装入最大价值为 8。
    第 2 次选择宝物 10,剩余容量 28−5=23,目前装入最大价值为 8+15=23。
    第 3 次选择宝物 6,剩余容量 23−8=15,目前装入最大价值为 23+20=43。
    第 4 次选择宝物 3,剩余容量 15−9=6,目前装入最大价值为 43+18=61。

    第 5 次选择宝物 5,剩余容量 6−5=1,目前装入最大价值为 61+8=69。
    第 6 次选择宝物 8,发现上次处理完时剩余容量为 1,而 8 号宝物重量为 4,无法全部
    放入,那么可以采用部分装入的形式,装入 1 个重量单位,因为 8 号宝物的单位重量价值为
    1.5,因此放入价值 1×1.5=1.5,你也可以认为装入了 8 号宝物的 1/4,目前装入最大价值为
    69+1.5=70.5,剩余容量为 0。
    (3)构造最优解
    把这些放入的宝物序号组合在一起,就得到了最优解(2,10,6,3,5,8),其中最后
    一个宝物为部分装入(装了 8 号财宝的 1/4),能够装入宝物的最大价值为 70.5。

    伪代码详解
    (1)数据结构定义
    根据算法设计中的数据结构,我们首先定义一个结构体 three:
    struct three{
    double w; //每种宝物的重量
    double v; //每种宝物的价值
    double p; //每种宝物的性价比(价值/重量)

    (2)性价比排序
    我们可以利用 C++中的排序函数 sort(见附录 B),对宝物的性价比从大到小(非递增)
    排序。要使用此函数需引入头文件:
    #include <algorithm>
    语法描述为:
    sort(begin, end)// 参数 begin 和 end 表示一个范围,分别为待排序数组的首地址和尾地址
    在本例中我们采用结构体形式存储,按结构体中的一个字段,即按性价比排序。如果不
    使用自定义比较函数,那么 sort 函数排序时不知道按哪一项的值排序,因此采用自定义比较
    函数的办法实现宝物性价比的降序排序:
    bool cmp(three a,three b)//比较函数按照宝物性价比降序排列
    {
    return a.p > b.p; //指明按照宝物性价比降序排列
    }
    sort(s, s+n, cmp); //前两个参数分别为待排序数组的首地址和尾地址
    //最后一个参数 compare 表示比较的类型
    (3)贪心算法求解
    在性价比排序的基础上,进行贪心算法运算。如果剩余容量比当前宝物的重量大,则可以放入,剩余容量减去当前宝物的重量,已放入物品的价值加上当前宝物的价值。如果剩余
    容量比当前宝物的重量小,表示不可以全部放入,可以切割下来一部分(正好是剩余容量),
    然后令剩余容量乘以当前物品的单位重量价值,已放入物品的价值加上该价值,即为能放入
    宝物的最大价值。
    for(int i = 0;i < n;i++)//按照排好的顺序,执行贪心策略
    {
    if( m > s[i].w )//如果宝物的重量小于毛驴剩下的运载能力,即剩余容量
    {
    m -= s[i].w;
    sum += s[i].v;
    }
    else //如果宝物的重量大于毛驴剩下的承载能力
    {
    sum += m 乘以 s[i].p; //进行宝物切割,切割一部分(m 重量),正好达到驴子承重
    break;
    }
    }

    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int M=1000005;
    struct three{
        double w;//每个宝物的重量
        double v;//每个宝物的价值
        double p;//性价比
    }s[M];
    bool cmp(three a,three b)
    {
        return a.p>b.p;//根据宝物的单位价值从大到小排序
    }
    int main()
    {
        int n;//n 表示有 n 个宝物
        double m ;//m 表示毛驴的承载能力
        cout<<"请输入宝物数量 n 及毛驴的承载能力 m :"<<endl;
        cin>>n>>m;
        cout<<"请输入每个宝物的重量和价值,用空格分开: "<<endl;
        for(int i=0;i<n;i++)
        {
            cin>>s[i].w>>s[i].v;
            s[i].p=s[i].v/s[i].w;//每个宝物单位价值
        }
        sort(s,s+n,cmp);
        double sum=0.0;// sum 表示贪心记录运走宝物的价值之和
        for(int i=0;i<n;i++)//按照排好的顺序贪心
        {
            if( m>s[i].w )//如果宝物的重量小于毛驴剩下的承载能力
        {
            m-=s[i].w;
            sum+=s[i].v;
        }
            else//如果宝物的重量大于毛驴剩下的承载能力
            {
                sum+=m * s[i].p;//部分装入
                break;
            }
        }
        cout<<"装入宝物的最大价值 Maximum value="<<sum<<endl;
        return 0;
    }

    附:本题是个DP问题,用贪心法并不一定可以求得最优解,以后了解了动态规划算法后本题就有了新的解法

    贪心算法示例点击打开链接

  • 相关阅读:
    DGL学习(六): GCN实现
    DGL学习(五): DGL构建异质图
    DGL学习(四): 图分类教程
    LuoGuP2495:[SDOI2011]消耗战
    LuoGuP1121:环状最大两段子段和
    LuoGuP3177:[HAOI2015]树上染色
    LuoGuP2607:[ZJOI2008]骑士
    HDU4283:You Are the One
    LuoGuP4294:[WC2008]游览计划
    LuoGuP4127:[AHOI2009]同类分布
  • 原文地址:https://www.cnblogs.com/aerer/p/9931068.html
Copyright © 2011-2022 走看看