首页 > 编程语言 >【技术积累】算法中的贪心算法【一】

【技术积累】算法中的贪心算法【一】

时间:2023-06-08 23:34:42浏览次数:44  
标签:积累 city max 算法 intervals 区间 total 贪心

贪心算法是什么

贪心算法是一种常见的算法思想,主要应用于优化问题中,特别是在计算机科学和运筹学领域中。贪心算法的核心思想是每一步都选择当前最好的选项,从而得到全局最优解。

贪心算法通常包括以下步骤:

  1. 确定问题的最优子结构:即在问题中寻找那些可以自行解决的子问题。

  2. 开始构建解决方案:从问题的初始状态开始,按照某种规则选择一个最优解,并将其添加到中间方案中。该步骤不断重复,直到找到全局最优解。

  3. 判断可行性:为了确保得到一个全局最优解,需要在每个构建解决方案的步骤中,检查得到的局部最优解是否是可行的。如果当前的局部最优解无法满足问题的限制条件,则需要放弃此局部最优解,重新开始构建方案。

贪心算法的优点是输入数据越大,运行时间越短;同时,由于贪心算法的设计都是局部的最优决策,不是全局的最优决策,因此可能不会得到最优解,但通常会得到接近最优解的解决方案。

贪心算法适用于一些特殊的算法场景,如图论中的最小生成树算法、哈夫曼编码等。同时,在一些工业设计、物流计划及经济学领域中也有应用。

贪心算法需要注意的问题是不能保证一定得到全局最优解,有可能会导致次优解的出现。因此,在具体应用中,需要充分了解问题的性质,深入分析问题才能设计出较好的贪心算法。

旅行商问题

一个旅行商要拜访n个城市,求他走的最短路径。

解题思路:

  1. 随意选择一个城市作为起点
  2. 从该城市出发,依次经过还未访问的最近的城市
  3. 计算路径长度,并记录已访问的城市
  4. 重复步骤2-3,直到所有城市都被访问
  5. 返回起点城市,路径长度即为最短路径
// cities为城市数量,dist为城市间距离矩阵
function TSP (cities, dist)
    visited = [false] * cities // 初始化所有城市未被访问
    current_city = 0 // 从城市0开始
    visited[current_city] = true // 标记当前城市为已访问
    path = [current_city] // 记录遍历路径
    total_distance = 0 // 路径总距离
    while true:
        if len(path) == cities: // 若所有城市都已访问过,则返回起点城市并计算路径总距离
            total_distance += dist[current_city][0] // 加上最后一个城市到起点城市的距离
            path.append(0)
            return path, total_distance
        next_city = -1 // 下一个要访问的城市
        min_distance = Inf // 到下一个城市路径的最小距离
        for i in range(cities):
            if not visited[i] and dist[current_city][i] < min_distance:
                next_city = i
                min_distance = dist[current_city][i]
        current_city = next_city // 更新当前城市
        visited[current_city] = true // 标记新城市为已访问
        path.append(current_city) // 记录经过的城市
        total_distance += min_distance // 累计最小距离

部分背包问题

有n个物品和一个容量为C的背包,每个物品都有自己的价值和重量,求装入背包的物品的最大价值。

1.计算每个物品的性价比(价值/重量)。

2.将物品按性价比从高到低排序。

3.从性价比最高的物品开始,依次放入背包,直到背包装满或所有物品都放入背包。

function fractional_knapsack(n, item, C)
// n表示物品数量,item为物品数组,C为背包容量
for i from 1 to n do
item[i].ratio = item[i].value / item[i].weight
// 计算每个物品的性价比


sort item by decreasing ratio
// 将物品按性价比从高到低排序

total_value = 0
for i from 1 to n do
    if C >= item[i].weight then
        total_value += item[i].value
        C -= item[i].weight
        // 如果背包容量可以放下物品i,则将物品i完全放入背包
    else
        total_value += C * item[i].ratio
        break
        // 否则将物品i按比例分割,在背包中放入一部分
        // 直到背包装满或物品i全部放入

return total_value
// 返回装入背包的物品的最大价值

区间调度问题

给定n个区间,求用尽可能少的区间覆盖整个区间的最大数量。

  1. 首先按照区间结束时间的顺序将所有区间排序(从小到大),设排序后的区间序列为intervals。
  2. 初始化变量end为区间intervals[0]的结束时间,计数器count为1,表示第一个区间一定要选。
  3. 遍历排序后的区间序列intervals,如果当前区间的开始时间大于等于end,则选择该区间,将end更新为该区间的结束时间,计数器count加1。
  4. 最后输出计数器count即为最大数量。
sort(intervals) // 对区间按照结束时间进行排序
end = intervals[0].end // 初始化end为第一个区间的结束时间
count = 1 // 初始化计数器count为1
for i in range(1, intervals.size()):
if intervals[i].start >= end:
count += 1
end = intervals[i].end
print(count) // 输出最大数量

最小罚款问题

某市道路有n个路口需要维修,第i个路口在时间ti - li到ti + li之间维修,若在该时段经过会被罚款wi。求如何安排维修时间,使得罚款总额最小。

  1. 将每个路口按照维修起始时间递增排序
  2. 遍历所有路口,维护一个区间集合,表示当前需要维修的路口时间段
  3. 对于每个路口,如果它的维修时间段与当前区间集合存在交集,则将交集部分取出,并且计算该部分的罚款总额
  4. 将该路口的维修时间段加入当前区间集合,维护集合的增序,重复步骤3,直至处理完所有路口
# 将每个路口按照维修起始时间递增排序
sorted_intervals = sorted(intervals, key=lambda x: x[0])

# 初始:空集合 s,罚款总额 total = 0
s = set()
total = 0

# 遍历所有路口
for interval in sorted_intervals:
    # 维修时间段 [ti-li, ti+li] 表示为区间 [l,r]
    l, r, w = interval

    # 逐个处理当前区间集合中的所有区间
    remove_intervals = set()
    for i in s:
        # 计算 区间 interval 与 i 的交集
        a, b = max(l, i[0]), min(r, i[1])
        if a <= b:
            # 将交集 [a,b] 内的路口从集合 s 中删除
            remove_intervals.add(i)
            # 将交集内的罚款总额加入 total
            total += w * (b - a + 1)

    # 从集合 s 中删除所有交集区间
    s -= remove_intervals
    # 将区间 [l,r] 加入集合 s
    s.add((l, r))

# 对于集合 s 中所有区间,以左端点为第一关键字,右端点为第二关键字进行排序
s = sorted(s, key=lambda x: (x[0], x[1]))

# 返回罚款总额
return total

跳跃游戏

给定一个数组,数组中的每个元素代表你在该位置可以跳跃的最大长度,求是否可以到达最后一个元素。

  1. 记录一个变量max_reach表示当前所能到达的最远距离,初始值为第一个元素的距离。

  2. 对数组从第二个元素开始遍历: a. 如果当前位置超出了max_reach的范围,则说明无法到达最后一个元素,返回false。 b. 否则,将当前位置能到达的最远距离和max_reach取最大值,更新max_reach。

  3. 遍历结束后,如果max_reach能够到达最后一个元素,则返回true;否则,返回false。

function canJump(nums) {
    let max_reach = nums[0];
    for (let i = 1; i < nums.length; i++) {
        if (i > max_reach) {
            return false;
        }
        max_reach = Math.max(max_reach, i + nums[i]);
    }
    return max_reach >= nums.length - 1;
}

化学物质混合问题

有n种化学物质,需要混合制成一种新的化学物质,各种化学物质有自己的份量和价格,求最小的制作成本。

  1. 首先将各种化学物质按价格从小到大排序。

  2. 然后从价格最低的化学物质开始,依次按其份量的比例将其混合到目标物质中。

  3. 如果已混入的各种化学物质份量之和等于目标物质的总份量,则制作完成;否则继续将价格次低的化学物质混入。

  4. 直到制作完成或者所有化学物质都已混入为止。

// 输入:
// chemicals: 化学物质数组,包括每种物质的份量和价格
// target_amount: 目标物质的总份量
// 注:代码中的by_price为排序关键字,需要根据具体实现进行定义。


function mixedChemicals(chemicals[], target_amount):
  // 按价格从小到大排序
  sort(chemicals, by_price)

  i = 0 // 当前混入的化学物质下标
  total = 0 // 已混入的各种化学物质总份量之和
  cost = 0 // 制作成本

  // 按比例依次混入各种化学物质
  while (total < target_amount) and (i < len(chemicals)):
    // 每次混入化学物质的份量
    amount = min(target_amount - total, chemicals[i].amount)
    // 每次混入的成本
    unit_cost = chemicals[i].price / chemicals[i].amount
    // 更新总成本
    cost += amount * unit_cost
    // 更新已混入的总份量
    total += amount
    // 更新当前混入的化学物质下标
    i += 1

  // 判断是否制作成功
  if total == target_amount:
    return cost
  else:
    return '制作失败'

资源分配问题

  1. 给定n个资源和m个任务,每个任务需要一定量的资源,其中一些任务是必须完成的,如何分配资源使得完成必须任务的代价最小。
  2. 将所有任务按是否为必须任务分成两组:必须完成的任务和非必须任务。
  3. 对必须完成的任务按照所需资源从大到小排序。
  4. 从资源数最大的必须任务开始,依次分配资源,直到分配完毕或无法完成必须任务。
  5. 对剩余的非必须任务按照所需资源从大到小排序。
  6. 依次给非必须任务分配资源,直到分配完毕或无法完成任务。
//将所有任务按是否为必须任务分成两组:必须完成的任务和非必须任务。
for each task:
if task is mandatory:
add task to mandatory_tasks
else:
add task to optional_tasks



//对必须完成的任务按照所需资源从大到小排序。
sort(mandatory_tasks, by resource needed, descending)



//从资源数最大的必须任务开始,依次分配资源,直到分配完毕或无法完成必须任务。
for each task in mandatory_tasks:
if task can be completed:
allocate resources to task
else:
break



//对剩余的非必须任务按照所需资源从大到小排序。
sort(optional_tasks, by resource needed, descending)



//依次给非必须任务分配资源,直到分配完毕或无法完成任务。
for each task in optional_tasks:
if task can be completed:
allocate resources to task
else:
break

 

标签:积累,city,max,算法,intervals,区间,total,贪心
From: https://www.cnblogs.com/yyyyfly1/p/17467978.html

相关文章

  • 【数据结构与算法】算法的时间复杂度和空间复杂度
    前言关于时空复杂度的分析,是每一个程序员的必备技能,本文将带你了解什么是时空复杂度?熟知怎样去计算一个算法的时间复杂度和空间复杂度。1.算法效率1.1.如何衡量一个算法的好坏如何衡量一个算法的好坏呢?我们先看一段代码:intFib(intN){if(N<3)return1;......
  • 安卓逆向 -- Frida Hook某车_sign算法分析
    接上节课内容安卓逆向--Frida环境搭建(HOOK实例)安卓逆向--FridaHook某车udid的加密值安卓逆向--FridaHook分析3DES加密值安卓逆向--SO文件逆向分析一、上节课抓包有个sign值二、用jadx打开,全局搜索"_sign",发现sign值都来自于toSign函数三、查找toSign函数声明的地方public......
  • 180116 EM算法资料整理(博客、论文、工具包、视频、书籍、代码,更新ing)
    BlogsHindon和Jordan理解的EM算法ComputationalStatisticsinPythonEM算法及其推广EM算法及其推广学习笔记从最大似然到EM算法浅解EM算法在缺失数据下的极大似然估计R代码Matlab极大似然估计缺失数据Cos424:InteractingwithDataProbabilityCourse关于EM算法的一些......
  • 文心一言 VS 讯飞星火 VS chatgpt (35)-- 算法导论5.3 5题
    五、证明:在过程PERMUTE-BY-SORTING的数组P中,所有元素都唯一的概率至少是1-1/n。文心一言:证明:在过程PERMUTE-BY-SORTING中,对于输入数组P中的每个元素,其出现次数为1,因此对于每个元素,在排序后的数组中,它出现的位置是唯一的。假设在排序后的数组中,有两个元素a和b,它们在原始数组......
  • 【计算机视觉】基于纹理特征的指纹识别位置与姿态算法
    简介计算机视觉基于纹理特征的指纹识别中的位置和姿态估计是指确定指纹图像中指纹的位置和方向的过程。这些信息对于后续的特征提取和匹配非常重要。下面介绍两个常用的算法来实现位置和姿态的估计:方向图和边界框。方向图(OrientationMap)方向图(OrientationMap)是用来表示指纹图像中......
  • 得到第K个大的数算法研究
      第一种算法是最容易想到的,就是利用快速排序的思想,将一个数组分成以某一个数X为轴,左边的所有的数都比X小,而右边的数都比X大。但我快速排序不同的是,在这个算法中只考虑X的一边,而不是两边都考虑。 如果X的位置是i,那么要得到第k个数,如果k<=i,那么这个数一定在i的左边,否则在i......
  • 从零开始学Java之查找算法有哪些?
    前言在前面的两篇文章中,给大家介绍了常见的排序算法,除此之外,其实还有查找算法也需要我们掌握。接下来就来给大家讲讲都有哪些查找算法,以及经典的二分查找法该如何实现。全文大约【3000】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更......
  • 医疗图像方向硕士,焦虑发论文毕业,咨询好的CV算法方向,与同门如何合作?
    第一、目前CV算法哪个方向好发文章,怎么快速准备并上手赶出论文?这个问题不是特别好准确回答,因为CV算法是一个非常大研究领域,包括目标检测,图像分割,图像生成,3D目标检测,三维图像重建,图像去雾,图像超分辨率等非常多的方向。你会这么问,我的感觉是你对其中哪个方向研究都不会很深,因为你是......
  • 机器学习算法(一)之KNN算法理论
    KNN算法又称为K近邻算法,是机器学习中比较简单的数据挖掘算法,其基本思想也简单,将“距离相近的,判定为同一类”,在整个样本空间中,k个最为接近的样本,大多数属于同一类别。对其进行一个细致的理解如下:如果在操场中心有一只羊,在以它为中心的周围,分布着奶牛和山羊,现在要对这只羊进行分类,......
  • 关于CV算法岗就业相关问题,精华回答分享
    粉丝提问:你好,看星球上做前端,后端,java的人比较多,好像没有看到有多少人做算法,我现在已经毕业了,是一名cv算法工程师,但是我现在很苦恼,感觉自己代码能力很弱,每次都是拿别人的开源代码跑一跑,不会复现论文,也不知道怎么做优化,想请教一下,该怎么去培养自己复现论文的能力,以及怎么去做算法......