• 贪心算法总结


                                           贪心算法总结
    

    一、算法思想
    贪心法的基本思路:
    从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。


    该算法存在问题:
    1. 不能保证求得的最后解是最佳的。
    2. 不能用来求最大或最小解问题;
    3. 仅仅能求满足某些约束条件的可行解的范围。
    实现该算法的过程:
    从问题的某一初始解出发;
    while 能朝给定总目标前进一步;
    求出可行解的一个解元素;
    由所有解元素组合成问题的一个可行解;

    二、ACM做题情况
    本专题,学习贪心算法,所给的18道题中仅仅AC了14道(当中还有两题同样)。除了几道水题以外。大部分题目是用到贪心算法来解决的,做了这套题,尽管感觉非常困难,但想办法还是能AC几道题的,做过这些题目使我对贪心算法印象加深,没有曾经感觉那么抽象,算是有进步吧,但还是不熟练,还须要加强训练。

    简短截说。还是在复习回想一下,关于贪心算法的经典题目吧。

    三、典型例题分类分析

    【背包问题】

    给定一个载重量为M的背包。考虑n个物品,当中第i个物品的重量 ,价值wi (1≤i≤n),要求把物品装满背包,且使背包内的物品价值最大。
    有两类背包问题(依据物品能否够切割)。假设物品不能够切割,称为0—1背包问题(动态规划)。假设物品能够切割,则称为背包问题(贪心算法)。

    有3种方法来选取物品:
    (1)当作0—1背包问题。用动态规划算法,获得最优值220;
    (2)当作0—1背包问题。用贪心算法。按性价比从高究竟顺序选取物品,获得最优值160。

    因为物品不可切割。剩下的空间白白浪费。
    (3)当作背包问题。用贪心算法。按性价比从高究竟的顺序选取物品,获得最优值240。

    因为物品能够切割,剩下的空间装入物品3的一部分。而获得了更好的性能。

    struct bag{
        int w;          //物品的重量
        int v;          //物品的价值
        double c;       //性价比
    }a[1001];           //存放物品的数组
    排序因子(按性价比降序):
    bool cmp(bag a, bag b){
        return a.c >= b.c;
    }
    //形參n是物品的数量,c是背包的容量M,数组a是按物品的性价比降序排序
    double knapsack(int n, bag a[], double c)
    {
      double cleft = c;        //背包的剩余容量
      int i = 0;
      double b = 0;          //获得的价值
      //当背包还能全然装入物品i
      while(i<n && a[i].w<cleft)
      {
        cleft -= a[i].w;
        b += a[i].v;
        i++;
      }
      //装满背包的剩余空间
      if (i<n) b += 1.0*a[i].v*cleft/a[i].w;
      return b;
    }
    假设要获得解向量,则须要在数据结构中增加物品编号:
    struct bag{
        int w;
        int v;
        double x;       //装入背包的量。0≤x≤1
        int index;      //物品编号
        double c;
    }a[1001];
    double knapsack(int n, bag a[], double c)
    {
      double cleft = c;
      int i = 0;
      double b = 0;
      while(i<n && a[i].w<=cleft)
      {
        cleft -= a[i].w;
        b += a[i].v;
        //物品原先的序号是a[i].index,所有装入背包
        a[a[i].index].x = 1.0;
        i++;
      }
      if (i<n) {
        a[a[i].index].x = 1.0*cleft/a[i].w;
        b += a[a[i].index].x*a[i].v;
      }
      return b;
    }

    【找零钱问题】

    问题描写叙述:
    当前有面值分别为2角5分,1角。5分,1分的硬币,请给出找n分钱的最佳方案(要求找出的硬币数目最少)
    问题分析:
    依据常识,我们到店里买东西找钱时。老板总是先给我们最大面值的,要是不够再找面值小一点的,直到找满为止。假设老板都给你找分数的或者几角的。那你肯定不干。另外,他也可能没有那么多零碎的钱给你找。事实上这就是一个典型的贪心选择问题。


    问题的算法设计与实现:
    先举个样例。假如老板要找给我99分钱,他有上面的面值分别为25,10,5。1的硬币数,为了找给我最少的硬币数,那么他是不是该这样找呢。先看看该找多少个25分的。 99/25=3。好像是3个,要是4个的话,我们还得再给老板一个1分的。我不干。那么老板仅仅能给我3个25分,因为还少给我24,所以还得给我2个10分的和4个1分。


    详细实现伪代码

    //找零钱算法 
    //By falcon 
    //输入:数组m,依次存放从大到小排列的面值数。n为须要找的钱数,单位所有为分 
    //输出:数组num,对比数组m中的面值存放不同面值的硬币的个数。即找钱方案
    比方要找N分钱。先拿N除最大零钱面值,能够取模得出余数。 
    当然取整就是所找的最大面值零钱的个数。 
    所得余数再次处理,用的是一个循环结构。 
    N输入取值 
    M是定义的面值M[0]是最大面值 
    K是一个数组,存储各面值零钱的个数 
    i=0 
    do while (N>0) 
    K[0]=int(N/M[i]) 
    N=mod(N,M[i]) 
    i++ 
    end do

    【钓鱼问题】

    问题描写叙述:
    约翰有h(1≤h≤16)个小时的时间,在该地区有n(2≤n≤25)个湖,这些湖刚好分布在一条路线上,该路线是单向的。约翰从湖1出发,他能够在任一个湖结束钓鱼。

    但他仅仅能从一个湖到达还有一个与之相邻的湖,并且不必每一个湖都停留。


    假设湖i(i=1~n—1),以5分钟为单位,从湖i到湖i+1须要的时间用ti(0<ti≤192)表示。比如t3=4,是指从湖3到湖4须要花20分钟时间。
    已知在最初5分钟。湖i估计钓到鱼的数量为fi(fi≥0)。以后每隔5分钟,估计钓到鱼的数量将以常数di(di≥0)递减。

    假设某个时段估计钓到鱼的数量小于或等于di,那么在下一时段将钓不到鱼。为简单起见,假设没有其他的钓鱼者影响约翰的钓鱼数量。
    编敲代码,帮助约翰制定钓鱼旅行的计划,以便尽可能多的钓到鱼。
    问题要求:
    输入
    对每组測试例。第一行是n,接下来一行是h。 以下一行是n个整数fi(1≤i≤n)。然后是一行n个整数di (1≤i≤n),最后一行是n—1个整数ti(1≤i≤n—1)。
    输入
    对每组測试例。第一行是n,接下来一行是h。 以下一行是n个整数fi(1≤i≤n)。然后是一行n个整数di (1≤i≤n)。最后一行是n—1个整数ti(1≤i≤n—1)。


    对每一个測试例,输出在每一个湖上花费的时间。这是约翰要实现钓到最多的鱼的计划(必须使整个计划在同一行输出)。接下来一行是钓到的鱼的数量。

    (假设存在非常多方案。尽可能选择在湖1钓鱼所耗费的时间。即使有些时段没有钓到鱼;假设还是无法区分,那就尽可能选择在湖2钓鱼所耗费的时间,以此类推。)
    问题分析:
    1)数据结构
    每一个湖估计钓到鱼的数量,定义为数组:#define NUM 30
    int f[NUM];
    每一个湖估计钓到鱼的数量的递减值,定义为数组:
    int d[NUM];
    相邻湖之间的旅行时间,定义为数组:
    int t[NUM];
    钓鱼计划,定义为数组:
    int plan[NUM];
    湖的个数n,用于钓鱼的时间h,尽可能多的钓鱼数量best。
    2)枚举在随意一个湖结束钓鱼时的最优钓鱼计划
    首先把用于钓鱼的时间h,由小时转换为以5分钟为单位的时间: h=h×60/5;
    这样把钓5分钟鱼的时间称为钓一次鱼。因为约翰从湖1出发。能够在任一个湖结束钓鱼,要得到最优解,就须要进行搜索。
    3)採用贪心策略,每次选择鱼最多的湖钓一次鱼
    对于每一个湖来说,因为在不论什么时候鱼的数目仅仅和约翰在该湖里钓鱼的次数有关,和钓鱼的总次数无关,所以这个策略是最优的。一共能够钓鱼time次,每次在n个湖中选择鱼最多的一个湖钓鱼。
    採用贪心算法构造约翰的钓鱼计划。
    能够觉得约翰能从一个湖“瞬间转移”到还有一个湖,即在随意一个时刻都能够从湖1到湖pos中任选一个钓一次鱼。


    过程实现代码:

    //从湖1起到湖pos止,花费时间time(不含路程)的钓鱼计划
    void greedy(int pos, int time)
    { 
      if (time <= 0) return; //时间已经用完
      int i, j;
      int fish[MAXN];
      int p[MAXN];
      int t = 0; 
      for (i = 0; i < pos; ++i) 
        fish[i] = f[i]; 
      memset(p, 0, sizeof(p)); 
      ……
    }
    //在时间time内,选择鱼最多的湖钓鱼;假设鱼都没有了,就把时间放在湖1上
    for (i = 0; i < time; ++i)
    { 
      int max = 0;      //鱼最多的湖中,鱼的数量
      int id = -1;     //鱼最多的湖的编号
      //查找鱼最多的湖中。鱼的数量和湖的编号
      for (j = 0; j < pos; ++j)
        if (fish[j] > max){ 
          max = fish[j]; 
          id = j; 
        } 
      if (id != -1)      //找到了,进行钓鱼处理
      {
        ++p[id]; 
        fish[id] -= d[id]; 
        t += max; 
      }
      //没有找到(从湖1起到湖pos所有钓完了),就把时间放在湖1上
      else ++p[0]; 
    } 
    //处理最优方案
    if (t > best)
    { 
      best = t;         //最优值
      memset(plan, 0, sizeof(plan));
      for (i = 0; i < pos; ++i)  //最优解
        plan[i] = p[i]; 
    }
    输出钓鱼计划时。再把5乘回去。就变成实际的钓鱼时间(分钟):
    for (i=0; i<n-1; ++i) 
        printf("%d, ", plan[i] * 5);
    printf("%d
    ", plan[n-1] * 5); 
    printf("Number of fish expected: %d
    ", best);
    

    实现代码:

    #include <iostream>
    #include <queue>
    using namespace std;
    struct data {
           int f,d,id;
    }a[30],b;
    int n,t,tran[30],x,ans,maxi,save[30],tmp[30],tt;
    priority_queue <data> q;
    bool operator<(data a,data b) {
         if (a.f==b.f) return a.id>b.id;
         else return a.f<b.f;
    }
    int main() {
        while (~scanf("%d",&n)) {
              if (n==0) break;
              scanf("%d",&t);
              t *= 12;
              for (int i=1;i<=n;i++)
                  scanf("%d",&a[i].f);
              for (int i=1;i<=n;i++) {
                  scanf("%d",&a[i].d);
                  a[i].id = i;
              }
              tran[1] = 0;
              for (int i=2;i<=n;i++) {
                  scanf("%d",&x);
                  tran[i] = tran[i-1] + x;
              }
              ans = -1;
              memset(save,0,sizeof(save));
              for (int i=1;i<=n;i++) {
                  maxi = 0;
                  memset(tmp,0,sizeof(tmp));
                  tt = t - tran[i];
                  while (!q.empty()) q.pop();
                  for (int j=1;j<=i;j++)
                      q.push(a[j]);
              while (tt>0) {
                        b = q.top();
                        q.pop();
                        tt--;
                        maxi += b.f;
                        tmp[b.id]++;
                        b.f -= b.d;
                        if (b.f<=0) b.f = 0;
                        q.push(b);
                  }
              if (maxi>ans) {
                                ans = maxi;
                                for (int j=1;j<=i;j++)
                                    save[j] = tmp[j];
                  }
              }
    
              for (int i=1;i<n;i++)
                  printf("%d, ",save[i]*5);
              printf("%d
    ",save[n]*5);
              printf("Number of fish expected: %d
    
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    31 整数中1出现的次数(从1到n整数中1出现的次数)
    算法寒假实习面试经过之 十一贝(offer) 联想研究院(电话一面 被拒)
    百度feed 寒假实习 一面二面(offer)
    30连续子数组的最大和
    29最小的K个数
    28数组中出现次数超过一半的数字
    MySQL操作数据库和表的基本语句(DDL)
    Oracle————存储过程与函数
    Oracle清空数据库中数据表数据的方法
    Linux之常用Shell脚本总结
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8419386.html
Copyright © 2020-2023  润新知