贪心算法篇——万千抉择中的唯一考量,最优解追寻的跬步累积(1)

贪心算法篇——万千抉择中的唯一考量,最优解追寻的跬步累积(1)

文章目录

在这里插入图片描述

引言:在选择的海洋中

在人生的旅途上,每个人都要面临无数的选择。每一个选择,都是一次抉择;每一次抉择,都是命运的交汇点。数学与计算机科学的世界里,贪心算法正是对这种“选择”的一种深刻体现。在一系列的选择面前,贪心算法如同一位睿智的旅行者,始终秉持着最优的哲学:每一次决策都应基于局部最优,以期在最后抵达全局最优的境地。

贪心算法(Greedy Algorithm),正如其名所示,是一种每次都选择当前看起来最优解的算法。这种算法策略简单却充满智慧,常常能够解决很多看似复杂的问题。它通过一种局部的、贪婪的方式,一步步走向最终解。然而,正如智慧的旅行者需要对道路有所预见一样,贪心算法也有其适用的范围,只有在满足某些条件时,它才能发挥出最优解的魅力。

在这篇报告中,我们将深入探讨贪心算法的基本理念、适用范围、经典应用,并通过具体的代码示例,揭开这一算法的神秘面纱。

贪心算法的哲学:局部最优,全球最优

“从来没有人能够通过偷懒来获得全局的成功。”——贪心算法的核心思想可以总结为这一句话:
在每一个步骤中,我们都做出当前最优的选择,期待最后能获得最优的整体解。贪心算法仿佛在告诉我们,在面临选择时,我们无需过多的回顾过往,也不必过分担忧未来,我们只需要关注眼前,做出最合适的决策。

贪心算法的步骤

  • 问题分解:将问题分解为若干个子问题。
  • 贪心选择:在每个子问题中,选择一个局部最优的解。
  • 问题求解:通过局部最优的选择构建全局最优解。
  • 验证最优性:检查贪心选择是否真的能带来全局最优解。

适用条件:贪心选择性质和最优子结构
贪心算法能够成功地解决某些问题,但并非适用于所有问题。其适用条件是:

贪心选择性质:局部最优解可以构成全局最优解。即,做出局部最优决策的过程中,并不会错过最终最优解。

最优子结构:问题的最优解包含了子问题的最优解。换句话说,大问题的最优解可以通过合并其子问题的最优解获得。

只有在满足这两个条件时,贪心算法才能保证全局最优解。

贪心算法的经典应用

贪心算法并不是一种“万能钥匙”,它并不能解决所有问题。但在某些经典问题中,贪心算法往往能以一种简单而高效的方式,提供令人满意的答案。以下是一些典型的应用场景:

1. 活动选择问题

  • 问题描述:给定一组活动,每个活动都有一个开始时间和结束时间,如何选择活动使得所选活动互不重叠,且选择的活动数最大。
  • 贪心策略:每次选择结束时间最早的活动,以便为后续活动留出更多的时间。

2. 最小生成树问题
问题描述:给定一个图,如何选择一些边使得图成为连通图,且边的权值之和最小。

贪心策略:每次选择当前权值最小的边,加入生成树中。

3. 背包问题(贪心近似)

  • 问题描述:给定一个背包容量和若干物品,每个物品有重量和价值,如何选择物品放入背包使得总价值最大。
  • 贪心策略:每次选择单位重量价值最大的物品,直到背包满。

活动选择问题的实现:一次简单的抉择
为了更好地理解贪心算法的应用,以下我们通过“活动选择问题”来展示贪心算法的具体实现。

问题描述
假设有多个活动,每个活动都有一个开始时间和结束时间。我们希望选择尽可能多的活动,使得这些活动之间没有重叠。

贪心算法实现
我们可以根据活动的结束时间进行排序,每次选择结束时间最早的活动,并确保它与之前的活动不重叠。这个策略是贪心的,因为每次选择结束时间最早的活动能够为后续的活动留下最多的时间空间。

代码实现

#include<stdio.h>#include<stdlib.h>// 结构体定义,表示每个活动的开始时间和结束时间typedefstruct{int start;int finish;int index;} Activity;// 比较函数,用于根据活动的结束时间进行排序intcompare(constvoid*a,constvoid*b){return((Activity *)a)->finish -((Activity *)b)->finish;}// 活动选择函数,返回选择的活动索引voidactivity_selection(int start[],int finish[],int n){ Activity activities[n];// 将活动的开始时间、结束时间和索引存储到结构体数组中for(int i =0; i < n; i++){ activities[i].start = start[i]; activities[i].finish = finish[i]; activities[i].index = i;}// 按照结束时间进行排序qsort(activities, n,sizeof(Activity), compare);// 存储选择的活动int last_end_time =-1;// 记录最后一个选择活动的结束时间printf("选择的活动索引:");for(int i =0; i < n; i++){// 如果当前活动的开始时间大于等于上一个活动的结束时间if(activities[i].start >= last_end_time){// 选择该活动printf("%d ", activities[i].index); last_end_time = activities[i].finish;// 更新结束时间}}printf("\n");}intmain(){// 示例输入:活动的开始时间和结束时间int start[]={1,3,0,5,8,5};int finish[]={2,4,6,7,9,9};int n =sizeof(start)/sizeof(start[0]);// 计算活动的数量// 调用活动选择函数activity_selection(start, finish, n);return0;}

代码解析

  • 活动结构体:使用结构体 Activity 存储每个活动的开始时间 (start)、结束时间 (finish) 和索引 (index)。
  • 排序函数:compare 是一个用于 qsort 排序的比较函数。它根据活动的结束时间进行排序,确保我们可以优先选择结束时间最早的活动。
  • 活动选择:通过 qsort 排序后,我们遍历所有活动,选择那些开始时间大于等于最后选择活动结束时间的活动。
  • 输出选择的活动:在 activity_selection 函数中,我们选择符合条件的活动并打印其索引。

示例输出:

选择的活动索引:0 134

这意味着我们选择的活动是索引为 0, 1, 3, 4 的活动,它们的时间区间没有重叠。

贪心算法的局限与挑战

贪心算法的最大挑战在于如何做出正确的贪心选择。并非所有问题都能通过贪心策略得到全局最优解,尤其是在某些复杂问题中,局部最优解可能会导致整体的次优解

因此,在使用贪心算法时,我们需要仔细分析问题的结构,确保贪心选择性质和最优子结构成立。

结语:智者的选择,最优的未来

贪心算法,作为计算机科学中最具“智慧”的算法之一,它通过简洁直接的策略解决了许多看似复杂的问题。它的每一次选择,都是对局部最优的追求,而这一追求,最终汇聚成了全局最优的结果。在面对问题时,贪心算法教给我们一个深刻的道理——在合适的时刻,做出最好的选择,最终的道路会更加宽广和光明。

然而,正如人生中的许多选择并非总是简单易得,贪心算法并不是每一个问题的灵丹妙药。它适用于那些满足特定条件的问题,而对于其他问题,我们需要更加复杂和精密的算法。贪心算法不仅仅是一个算法,它更像是一种哲学,让我们在每一个选择的瞬间,都能以一种智慧的眼光看待前方的路。

本篇关于贪心算法的介绍就暂告段落啦,希望能对大家的的学习产生帮助,欢迎各位佬前来支持斧正!!!

在这里插入图片描述

Read more

Flutter 组件 spry 适配鸿蒙 HarmonyOS 实战:轻量化 Web 框架,构建高性能端侧微服务与 Middleware 治理架构

Flutter 组件 spry 适配鸿蒙 HarmonyOS 实战:轻量化 Web 框架,构建高性能端侧微服务与 Middleware 治理架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 spry 适配鸿蒙 HarmonyOS 实战:轻量化 Web 框架,构建高性能端侧微服务与 Middleware 治理架构 前言 在鸿蒙(OpenHarmony)生态迈向全场景分布式协同、涉及设备端侧 API 暴露、轻量化资源服务镜像及严苛的跨端 RPC 通信背景下,如何实现一套既能保持极低内存足迹(Footprint)、又能提供类似后端(Node.js/Koa)般丝滑开发体验且具备全异步处理能力的“端侧 Web 基座”,已成为决定应用分布式自治能力与全栈同构效率的关键。在鸿蒙设备这类强调 AOT 极致效能与背景任务严格限制的环境下,如果应用依然采用重量级的 HTTP 服务端,由于由于进程级的上下文切换开销,极易由于由于“算力溢出”导致鸿蒙应用在作为服务端响应时发生明显的电量损耗。 我们需要一种能够解耦路由逻辑、支持

By Ne0inhk

10、Vue3中Vuex从入门到实战:手写迷你Vuex,掌握前端状态管理核心

Vue3中Vuex从入门到实战:手写迷你Vuex,掌握前端状态管理核心 在Vue3项目开发中,组件化让代码复用和维护更高效,但跨组件、跨页面的数据共享却成了高频痛点——用户登录信息、全局权限、公共计数器等数据,如果靠组件传参层层传递,代码会变得混乱不堪。这时候,Vuex就成了前端状态管理的“大管家”,帮我们集中式管理共享数据。本文将从前端数据管理的痛点出发,带你吃透Vuex的核心用法,甚至手写一个迷你Vuex理解其底层原理。 一、前端数据管理:为什么需要Vuex? 现代Web应用由组件、数据、路由三大核心构成,组件内部的私有数据用ref/reactive管理即可,但共享数据的管理却需要更规范的方式。 我们先试想一个简单场景:用全局变量存储共享数据。 window._store ={}// 全局存储数据 这种方式看似简单,但存在致命问题:window._store不是响应式的,修改数据后Vue组件无法自动更新视图。如果我们用Vue的响应式API包裹全局数据,并提供统一的修改方法,这就是Vuex的雏形——本质是“响应式的全局数据 + 规范化的修改规则”。 二、Vuex是什

By Ne0inhk
【递归,搜索与回溯算法 & 记忆化搜索】深入理解记忆化搜索算法:记忆化搜索算法小专题

【递归,搜索与回溯算法 & 记忆化搜索】深入理解记忆化搜索算法:记忆化搜索算法小专题

前言:实现记忆化搜索的一般步骤      (1) 实现记忆化搜索代码步骤         (2) 如何将暴搜代码转换成记忆化搜索代码?         (3)如何添加一个备忘录?         斐波那契数     题目解析         算法原理         解法一:递归        时间复杂度高是因为递归展开树有很多次重复计算,我们可以优化这些重复的计算;我们可以创建一个备忘录,当计算其中一个分支时,把计算出的 d(i) 放入一个"备忘录"中 ( i = 1 ....... n ),当递归其他分支时,我们通过备忘录存储好的计算结果,减少递归树额外重复的展开;     解法二:记忆化搜索    当我们在递归的时候,发现递归过程会重复进行完全相同的问题,我们就把这些完全相同的问题存储到额外创建的"备忘录"中,再后续递归出现相同问题,直接从备忘录中拿计算好的结果即可,避免不必要的重复递归;  所以记忆化搜索,就是一个带备忘录的递归;记忆化搜索,其实也是剪枝的一种方式,在本题使用记忆化搜索,就能把指数级别的时间复杂度降到常数

By Ne0inhk
【踩坑记录】使用 Layui 框架时解决 Unity WebGL 渲染在 Tab 切换时黑屏问题

【踩坑记录】使用 Layui 框架时解决 Unity WebGL 渲染在 Tab 切换时黑屏问题

【踩坑记录】使用 Layui 框架时解决 Unity WebGL 渲染在 Tab 切换时黑屏问题 在开发 Web 应用时,尤其是集成了 Unity WebGL 内容的页面,遇到一个问题:当 Unity WebGL 渲染内容嵌入到一个 Tab 中时,切换 Tab 后画面会变黑,直到用户点击黑屏区域,才会恢复显示。 这个问题通常是因为 Unity 渲染在 Tab 切换时被暂停或未能获得焦点所致。 在本文中,我们将介绍如何在使用 Layui 框架时,通过监听 Tab 切换事件并强制 Unity WebGL 渲染恢复,来解决这一问题。 1. 问题描述 当 Unity WebGL 内容嵌入到页面中的多个

By Ne0inhk