算法设计与分析(贪心法)

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
应用实时监控服务-应用监控,每月50GB免费额度
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 【1月更文挑战第1天】在分析问题是否具有最优子结构性质时,通常先设出问题的最优解,给出子问题的解一定是最优的结论。证明思路是:设原问题的最优解导出子问题的解不是最优的,然后在这个假设下可以构造出比原问题的最优解更好的解,从而导致矛盾。(一个问题能够分解成各个子问题来解决,通过各个子问题的最优解能递推到原问题的最优解,此时原问题的最优解一定包含各个子问题的最优解,这是能够采用贪心法来求解问题的关键)贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择获得,即通过一系列的逐步局部最优选择使得最终的选择方案是全局最优的。

 

目录

一、贪心法的基本思想

二、贪心法的基本要素

1.最优子结构性质

2.贪心选择性质

三、贪心法的解题步骤及算法设计模式

步骤:

1.分解:

2.解决:

3.合并:

设计模式:

四、会场安排问题

五、最优装载问题

六、单元最短路径问题


一、贪心法的基本思想

贪心法是一种稳扎稳打的算法,他从问题的摸一个初始解出发,在每一个阶段都根据贪心策略来做出当前最优决策,逐步逼近给定目标,尽可能快地求得更好的解。当达到算法中的某一步不能再继续前进时,算法终止。也可以理解为:以逐步的局部最优,达到最终的全局最优。

二、贪心法的基本要素

1.最优子结构性质

       当一个问题的最优解一定包含其他子问题的最优解时,称此问题具有最优子结构性质。(一个问题能够分解成各个子问题来解决,通过各个子问题的最优解能递推到原问题的最优解,此时原问题的最优解一定包含各个子问题的最优解,这是能够采用贪心法来求解问题的关键)

       在分析问题是否具有最优子结构性质时,通常先设出问题的最优解,给出子问题的解一定是最优的结论。然后,采用反证法证明“子问题的解一定时最优的”结论成立。证明思路是:设原问题的最优解导出子问题的解不是最优的,然后在这个假设下可以构造出比原问题的最优解更好的解,从而导致矛盾。

2.贪心选择性质

       贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择获得,即通过一系列的逐步局部最优选择使得最终的选择方案是全局最优的。其中每次所做的选择,可以依赖于以前的选择,但不依赖于将来所做的选择。

       贪心选择性质所做的是一个非线性的子问题处理流程,即一个子问题并不依赖于另一个子问题,但是子问题间有严格的顺序性。

三、贪心法的解题步骤及算法设计模式

步骤:

1.分解:

将原问题分解为若干个相互独立的阶段。

2.解决:

对于每个阶段依据贪心策略进行贪心选择,求出局部的最优解。

3.合并:

将各个阶段的解合并为原问题的一个可行解。

设计模式:

Greedy(A,n)
{
    //A[0:n-1]包含n个输入,即A是问题的输入集合
    将解集合solution初始化为空;
    for(i=0;i<n;i++)                        //原问题分解为n个阶段
    {
        x=select(A);                        //依据贪心策略做贪心选择,求得局部最优解
        if(x可以包含在solution)              //判断解集合solution在加入x后是否满足约束条件
            solution=union(solution,x);    //部分局部最优解进行合并
    }
    return (解向量solution);                //n个阶段完成后,得到原问题的最优解
}

image.gif

四、会场安排问题

       此问题的核心思想是:每次从剩下未安排的会议中选择具有最早结束时间且不会与已安排的会议重叠的会议来安排。这样可以使下一个会议尽快开始。

1)实现活动安排问题的贪心算法。

测试数据可选用:

i

1

2

3

4

5

6

7

8

9

10

Begin

1

3

2

5

3

5

6

8

8

2

End

4

5

6

7

8

9

10

11

12

13

#include <iostream>
using namespace std;
void Select(int n, int A[],int B[], bool C[])
{
  int i, j;
  C[1] = true;
  j = 1, i = 2;
  while (i <= n)
  {
    if (A[i] > B[j]) {
      C[i] = true;
      j = i;
    }
    else {
      C[i] = false;
    }
    i++;
  }
};
int main()
{
  int A[11] = { 0,1,3,2,5,3,5,6,8,8,2 },
    B[11] = { 0,4,5,6,7,8,9,10,11,12,13 };
  bool C[11];
  Select(11, A, B, C);
  cout << "活动安排如下:" << endl;
  for (int k = 1; k <= 11; k++)
  {
    while (C[k])
    {
      cout << A[k]<<"  "<<B[k] << endl;
      break;
    }
  }
  return 0;
}

image.gif

运算截图如下:

image.gif

五、最优装载问题

2)实现最优装载的贪心算法。

测试数据可选用:

假设轮船限重12kg

i

1

2

3

4

5

Wi(kg)

8

4

2

5

7

#include "TanXin2.h"
#include<iostream>
using namespace std;
#define Max 10
typedef struct {
  int *elem;
  int length;
}SqList;
int InitList(SqList &L)//构造并初始化
{
  L.elem = new int[Max];
  if (!L.elem) return 0;
  L.length = 0;
  return 1;
}
void InputList(SqList &L,SqList &LB, int n)
{
  L.length = 0;
  if (n<0 || n>Max) return;
  cout << "请输入顺序表" << endl;
  for (int i = 0; i < n; i++)
  {
    cin >> L.elem[i];
    L.length++;
  }
  for (int j = 0; j < n; j++)
  {
    LB.elem[j] = L.elem[j];
    LB.length++;
  }
}
void OutputList(SqList L)
{
  for (int i = 0; i < L.length; i++)
  {
    cout << L.elem[i] << " ";
  }
}
void Compare(SqList &L)//冒泡排序
{
  int i, j, e;
  for (i = 1; i <= L.length; i++)
  {
    for (j = 0; j < L.length - i; j++)
    {
      if (L.elem[j] > L.elem[j + 1])
      {
        e = L.elem[j];
        L.elem[j] = L.elem[j + 1];
        L.elem[j + 1] = e;
      }
    }
  }
}
int LocateElem(SqList L, int e)//查找数据是否在顺序表内
{
  for (int i = 0; i < L.length; i++)
  {
    if (L.elem[i] == e) return i + 1;//查找成功,返回序号i+1
  }
  return 0;//查找失败,返回0
}
void Select(SqList &L,SqList &LB,int m)
{
  int sum = 0;
  for (int i = 0; i < L.length; i++)
  {
    if ((sum + L.elem[i]) < m)
    {
      sum += L.elem[i];
      cout<< LocateElem(LB, L.elem[i]) <<"  "<< L.elem[i] << endl;
    }
    else {
      break;
    }
  }
}
int main()
{
  int n = 5,m=12;
  SqList LA,LB;
  InitList(LA);
  InitList(LB);
  InputList(LA,LB,n);
  Compare(LA);
  cout << "所选择的为:" << endl;
  Select(LA,LB,m);
}

image.gif

代码运行截图如下:

image.gif

六、单元最短路径问题

3)单源最短路径的贪心算法。

测试数据可选用下图,1为源点:

image.gif编辑

#include<iostream>
#include<string>
using namespace std;
const int inf = INT_MAX;
void Dijkstra(int n, int source, int *dist, int *prev, int c[8][8])
{
  //表示点是否在s集合里
  bool *s=new bool[n];
  for (int i = 0; i < n; i++)
  {
    dist[i] = c[source][i];
    //初始化时,除了source外所有结点都不在s集合
    s[i] = i == source ? true : false;
    //结点可达,前驱结点记作源点
    //结点不可达,前驱结点记作-1
    prev[i] = dist[i] == inf ? -1 : source;
  }
  for ( i = 1; i < n; i++)//找n-1个点
  {
    int mindist = inf;
    int newnode;//记录距离source最近的点
    for (int j = 0; j < n; j++)
    {
      //j点不在s集合中,且到soutce的距离小于上一个点到source的距离
      if (!s[j] && dist[j] < mindist)
      {
        newnode = j;
        mindist = dist[newnode];
      }
    }
    s[newnode] = true;
    for ( j = 0;j < n; j++)//当s集合加入新结点时,需要更新dist和prev
    {
      if (!s[j] && c[newnode][j] < inf)
      {
        //点不在s中,且与新节点相邻
        int newdist = dist[newnode]+c[newnode][j];//新结点到source的距离+新结点到j点的距离
        if (newdist < dist[j])
        {
          dist[j] = newdist;
          prev[j] = newnode;//新结点成了前驱结点
        }
      }
    }
  }
}
int main()
{
  int c[8][8] = { 
    {0,2,inf,1,inf,3,inf,inf},
      {inf,0,6,inf,5,inf,inf,inf},
    {inf,inf,0,inf,inf,inf,inf,6},
    {inf,10,inf,0,inf,inf,2,inf},
    {inf,inf,9,inf,0,inf,inf,4},
    {inf,inf,inf,5,inf,0,4,inf},
    {inf,7,inf,inf,3,inf,0,8},
    {inf,inf,inf,inf,inf,inf,inf,0}
  };
  int n = 8;
  int dist[8];
  int prev[8];
  int source = 0;
  Dijkstra(8, source, dist, prev, c);
  cout << "源点到各点的最短路径为:";
  for (int i = 0; i < n; i++)
  {
    cout << dist[i] << " ";
  }
  return 0;
}

image.gif

运行结果如图所示:

image.gif


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
3月前
|
机器学习/深度学习 算法 搜索推荐
从理论到实践,Python算法复杂度分析一站式教程,助你轻松驾驭大数据挑战!
【10月更文挑战第4天】在大数据时代,算法效率至关重要。本文从理论入手,介绍时间复杂度和空间复杂度两个核心概念,并通过冒泡排序和快速排序的Python实现详细分析其复杂度。冒泡排序的时间复杂度为O(n^2),空间复杂度为O(1);快速排序平均时间复杂度为O(n log n),空间复杂度为O(log n)。文章还介绍了算法选择、分而治之及空间换时间等优化策略,帮助你在大数据挑战中游刃有余。
112 3
|
18天前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
27天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
35 6
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
82 1
|
3月前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
3月前
|
算法
PID算法原理分析
【10月更文挑战第12天】PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。
|
4月前
|
算法 搜索推荐 开发者
别再让复杂度拖你后腿!Python 算法设计与分析实战,教你如何精准评估与优化!
在 Python 编程中,算法的性能至关重要。本文将带您深入了解算法复杂度的概念,包括时间复杂度和空间复杂度。通过具体的例子,如冒泡排序算法 (`O(n^2)` 时间复杂度,`O(1)` 空间复杂度),我们将展示如何评估算法的性能。同时,我们还会介绍如何优化算法,例如使用 Python 的内置函数 `max` 来提高查找最大值的效率,或利用哈希表将查找时间从 `O(n)` 降至 `O(1)`。此外,还将介绍使用 `timeit` 模块等工具来评估算法性能的方法。通过不断实践,您将能更高效地优化 Python 程序。
81 4
|
4月前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
87 1
|
3月前
|
算法
PID算法原理分析及优化
【10月更文挑战第6天】PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。
|
3月前
|
算法 安全 Go
Python与Go语言中的哈希算法实现及对比分析
Python与Go语言中的哈希算法实现及对比分析
62 0