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

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
简介: 【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


相关实践学习
小试牛刀,一键部署电商商城
SAE 仅需一键,极速部署一个微服务电商商城,体验 Serverless 带给您的全托管体验,一起来部署吧!
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
20天前
|
数据采集 机器学习/深度学习 算法
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
53 4
|
1月前
|
存储 监控 算法
员工行为监控软件中的 Go 语言哈希表算法:理论、实现与分析
当代企业管理体系中,员工行为监控软件已逐步成为维护企业信息安全、提升工作效能的关键工具。这类软件能够实时记录员工操作行为,为企业管理者提供数据驱动的决策依据。其核心支撑技术在于数据结构与算法的精妙运用。本文聚焦于 Go 语言中的哈希表算法,深入探究其在员工行为监控软件中的应用逻辑与实现机制。
61 14
|
2月前
|
自然语言处理 算法 安全
境内深度合成服务算法备案通过名单分析报告
本报告基于《境内深度合成服务算法备案通过名单》,分析了2023年6月至2025年3月公布的10批备案数据,涵盖属地分布、行业应用及产品形式等多个维度。报告显示,深度合成算法主要集中于经济发达地区,如北京、广东、上海等地,涉及教育、医疗、金融、娱乐等多行业。未来趋势显示技术将向多模态融合、行业定制化和安全合规方向发展。建议企业加强技术研发、拓展应用场景、关注政策动态,以在深度合成领域抢占先机。此分析旨在为企业提供参考,助力把握技术发展机遇。
境内深度合成服务算法备案通过名单分析报告
|
2月前
|
供应链 算法 搜索推荐
从公布的前十一批其他算法备案通过名单分析
2025年3月12日,国家网信办发布算法备案信息,深度合成算法通过395款,其他算法45款。前10次备案中,深度合成算法累计3234款,其他类别647款。个性化推送类占比49%,涵盖电商、资讯、视频推荐;检索过滤类占31.53%,用于搜索优化和内容安全;调度决策类占9.12%,集中在物流配送等;排序精选类占8.81%,生成合成类占1.55%。应用领域包括电商、社交媒体、物流、金融、医疗等,互联网科技企业主导,技术向垂直行业渗透,内容安全和多模态技术成新增长点。未来大模型检索和多模态生成或成重点。
从公布的前十一批其他算法备案通过名单分析
|
2月前
|
人工智能 自然语言处理 供应链
从第十批算法备案通过名单中分析算法的属地占比、行业及应用情况
2025年3月12日,国家网信办公布第十批深度合成算法通过名单,共395款。主要分布在广东、北京、上海、浙江等地,占比超80%,涵盖智能对话、图像生成、文本生成等多行业。典型应用包括医疗、教育、金融等领域,如觅健医疗内容生成算法、匠邦AI智能生成合成算法等。服务角色以面向用户为主,技术趋势为多模态融合与垂直领域专业化。
|
3月前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
53 3
|
2月前
|
人工智能 自然语言处理 算法
从第九批深度合成备案通过公示名单分析算法备案属地、行业及应用领域占比
2024年12月20日,中央网信办公布第九批深度合成算法名单。分析显示,教育、智能对话、医疗健康和图像生成为核心应用领域。文本生成占比最高(57.56%),涵盖智能客服、法律咨询等;图像/视频生成次之(27.32%),应用于广告设计、影视制作等。北京、广东、浙江等地技术集中度高,多模态融合成未来重点。垂直行业如医疗、教育、金融加速引入AI,提升效率与用户体验。
|
4月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
5月前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
91 6
|
6月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
139 1