贪心算法总结

                                       贪心算法总结

一、算法思想

贪心法的基本思路:

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

该算法存在问题:

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\n", plan[n-1] * 5);
printf("Number of fish expected: %d\n", 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\n",save[n]*5);
          printf("Number of fish expected: %d\n\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zhchoutai/p/8419386.html

时间: 2024-10-10 10:01:25

贪心算法总结的相关文章

POJ1017 Packets(贪心算法训练)

Time Limit: 1000MS          Memory Limit: 10000K          Total Submissions: 51306          Accepted: 17391 Description A factory produces products packed in square packets of the same height h and of the sizes 1*1, 2*2, 3*3, 4*4, 5*5, 6*6. These pro

贪心算法的简述与示例

贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯.能够用贪心算法求解的问题一般具有两个重要特性:贪心选择性质和最优子结构性质. 参考:http://babybandf.blog.163.com/blog/static/61993532010112923767/ [例1]删数问题[B][/B] 试题描

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

五大常用算法之三贪心算法

贪心算法 贪心算法简介: 贪心算法是指:在每一步求解的步骤中,它要求"贪婪"的选择最佳操作,并希望通过一系列的最优选择,能够产生一个问题的(全局的)最优解. 贪心算法每一步必须满足一下条件: 1.可行的:即它必须满足问题的约束. 2.局部最优:他是当前步骤中所有可行选择中最佳的局部选择. 3.不可取消:即选择一旦做出,在算法的后面步骤就不可改变了. 贪心算法案例: 1.活动选择问题  这是<算法导论>上的例子,也是一个非常经典的问题.有n个需要在同一天使用同一个教室的活动a

零基础学贪心算法

本文在写作过程中参考了大量资料,不能一一列举,还请见谅.贪心算法的定义:贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解.贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关.解题的一般步骤是:1.建立数学模型来描述问题:2.把求解的问题分成若干个子问题:3.对每一子问题求解,得到子问题的局部最优解:4.把子问题的局部最优

贪心算法

一,贪心算法的设计思想 ? 从问题的某一个初始解出发逐步逼近给定的目标,每一步都作一个不可回溯的决策,尽可能地求得最好的解.当达到某算法中的某一步不需要再继续前进时,算法停止. 二,贪心算法的基本性质 1)贪心选择性质 所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到.这是贪心算法可行的第一个基本要素,也是贪心法与动态规划法的主要区别. 2) 最优子结构性质 该问题解的整体最优性依赖于其局部子问题解的最优性.这种性质是可以采用贪心算法解决问题的关键特征.例如

算法导论----贪心算法,删除k个数,使剩下的数字最小

先贴问题: 1个n位正整数a,删去其中的k位,得到一个新的正整数b,设计一个贪心算法,对给定的a和k得到最小的b: 一.我的想法:先看例子:a=5476579228:去掉4位,则位数n=10,k=4,要求的最小数字b是n-k=6位的: 1.先找最高位的数,因为是6位数字,所以最高位不可能在后5位上取到(因为数字的相对顺序是不能改变的,假设如果取了后五位中倒数第5位的7,则所求的b就不可能是6位的了,最多也就是4位的79228)理解这点很重要!所以问题变成从第1位到第k+1(n-(n-k-1))取

高级算法——贪心算法(找零问题)

function makeChange(origAmt, coins) {//贪心算法——找零问题 var remainAmt ; if (origAmt % .25 < origAmt) { coins[3] = parseInt(origAmt / .25); remainAmt = origAmt % .25; origAmt = remainAmt; } if (origAmt % .1 < origAmt) { coins[2] = parseInt(origAmt / .1); r

高级算法——贪心算法(背包问题)

贪心算法不能用来解决离散物品问题的原因是我们无法将“ 半台电视” 放入背包. 规则是按照物品价值高低顺序放入背包. function ksack(values, weights, capacity) { var load = 0; var i = 0; var v = 0; while (load < capacity && i < weights.length) { if (weights[i] <= (capacity - load)) { v += values[i

贪心算法换零钱(java)

贪心算法思想 贪心算法总是做出在当前看来做好的选择.也就是说贪心算法并不从整体最后考虑,他做出的选择只是局部最优选择.他所做出的仅是在某种意义上的局部最优解.贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解. 1.算法思路 贪心算法是一种不追求最优解,只希望得到较为满意解的方法.贪心算法一般可以快速得到满意的解,因为它省去了为找最优姐要穷尽所有肯呢个而必须耗费大量时间.贪婪(心)算法是一种改进了的分级处理方法.其核心是根据题意选取一种