贪心法讲解

news2024/11/19 18:45:30

目录

一、前言

二、贪心法

1、优缺点

2、例子:最少硬币问题

3、贪心和动态规划

4、例题:快乐司机(lanqiaoOJ题号1513)

5、例题:旅行家的预算(lanqiaoOJ题号775)

三、例题

1、翻硬币(lanqiaoOJ题号209)

(1)思路:BFS?

(2)思路:贪心

2、防御力(lanqiaoOJ题号226,2018年国赛)

(1)贪心法编程


一、前言

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

二、贪心法

1、优缺点

【算法优点】

容易理解:生活常见

操作简单:在每一步都选局部最优

效率高:复杂度常常是 O(1) 的

【算法缺点】

缺点:局部最优不一定是全局最优

2、例子:最少硬币问题

硬币面值 1、2、5。支付 13 元,要求硬币数量最少。

贪心:

(1)5元硬币,用2个

(2)2元硬币,用1个

(3)1元硬币用一个

硬币面值 1、2、4、5、6。支付9元。

贪心:

(1)6元硬币,1个

(2)2元硬币,1个

(3)1元硬币,1个。

错误!答案是:5元硬币+4元硬币

故硬币问题的正解应该是动态规划!

3、贪心和动态规划

贪心法求解的问题满足以下特征:

  • 最优子结构性质。当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质,也称此问题满足最优性原理。从局部最优能扩展到全局最优。
  • 贪心选择性质。问题的整体最优解可以通过一系列局部最优的选择来得到。

动态规划:

  • 重叠子问题:子问题是原大问题的小版本;计算大问题的时候,需要多次重复计算小问题。
  • 最优子结构:大问题的最优解包含小问题的最优解;可以通过小问题的最优解推导出大问题的最优解。

4、例题:快乐司机(lanqiaoOJ题号1513)

【题目描述】话说现在当司机光有红心不行,还要多拉快跑。多拉不是超载,是要让所载货物价值最大,特别是在当前油价日新月异的时候。司机所拉货物为散货,如大米、面粉、沙石、泥土… 现在知道了汽车核载重量为 w,可供选择的物品的数量 n。每个物品的重量为 gi,价值为 pi。求汽车可装载的最大价值。(n<10000, w<10000, 0<gi<=100, 0<=pi<=100)。

【输入描述】输入第一行为由空格分开的两个整数 n,w。第二行到第 n+1 行,每行有两个整数,由空格分开,分别表示 gi 和 pi。

【输出描述】最大价值(保留一位小数)。

n,w=map(int,input().split())
a=[]
for i in range(n):
    gp=list(map(int,input().split()))   #读重量、价值
    a.append(gp)
a.sort(key=lambda x:x[1]/x[0],reverse=True) #结构体排序,按单价从大到小排序
val=0   #最大价值
for k in a: #装货,从最贵的到最便宜的
    if k[0]<w:  #能装进卡车
        val+=k[1]
        w-=k[0]  #计算卡车余重
    else:
        val += w*k[1]/k[0]   #散货可以不装完
        break
print('%.1f'%val)

5、例题:旅行家的预算(lanqiaoOJ题号775)

【题目描述】

一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离 D1、汽车油箱的容量 C(以升为单位)、每升汽油能行驶的距离 D2、出发点每升汽油价格 P 和沿途油站数 N ( N可以为零 ),油站 i 离出发点的距离 Di、每升汽油价格Pi ( i=1, 2,...N ) 。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出 No Solution。

【输入描述】

第一行, D1, C, D2, P, N。接下来有 N 行。第 i+1 行,两个数字,油站 i 离出发点的距离 Di 和每升汽油价格Pi。

【输出描述】

输出所需最小费用,计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出 No Solution

贪心:找到下一个更便宜 (不是最便宜) 的加油站 k

  • 够开到 k,就只加刚好到 k 的油
  • 不够开到 k,就加满油

import sys
D1,C,D2,P,N=map(float,input().split())
N=int(N)
Di=[0]  #每个加油站到起点的距离,初始是起点
Pi=[0]  #每个加油站的油价,初始是起点油站的价格
for i in range(N):  #读油站距离和价格
    d,p=map(float,input().split())
    Di.append(d);Pi.append(p)
Di.append(D1);Pi.append(0)  #加终点

a=[0]*(N+1) #初始化每个加油站的加油量
DIS=C*D2    #满箱油能行驶距离
remain=0    #剩余油量
i=0
while i<=N:
    to_next=Di[i+1]-D[i]    #当前站点到下一站的距离
    if to_next>DIS:
        print('No Solution')
        sys.exit()      #不能到下一站,直接退出
    for k in range(i+1,N+2):    #搜索油价小于或等于i站的下一个油站k
        if Pi[k]<=Pi[i]:
            to_k=Di[k]-Di[i]    #当前站点到k站点的距离
            break
    if DIS>=to_k:       #一箱油能到k站
        oil=to_k/D2     #到k站需要多少油
        if remain>oil:  #剩下的油能直接到达k站,不用加油
            remain-=oil
        else:
            a[i]=oil-remain     #实际加油
        i=k     #现在到了k站
    else:   #不能到达k站,加满油去下一个站点
        a[i]=C-remain
        remain=C-to_next/D2
        i+=1
cost=0  #统计油钱
for i in range(N+1):
    cost+=a[i]*Pi[i]
print('%.2f'%cost)

三、例题

1、翻硬币(lanqiaoOJ题号209)

【题目描述】

小明玩"翻硬币"游戏。桌上放着排成一排的若干硬币。用 * 表示正面,用 o 表示反面 (是小写字母,不是零)。比如,可能情形是 **oo***oooo,如果同时翻转左边的两个硬币,则变为oooo***oooo。小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?约定:把翻动相邻的两个硬币叫做一步操作。

【输入格式】

两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度 < 1000.

【输出格式】

一个整数,表示最小操作步数。

【输入样例】

**********

o****o****

【输出样例】

5

(1)思路:BFS?

本题求从初始状态到目标状态的最短路径,非常符合 BFS 的特征。如果学过 BFS,很自然地会考虑用 BFS 来解题。但是本题的状态太多,用 BFS 肯定超时。

(2)思路:贪心

如果让小学生做这个游戏,他会简单地模拟翻动的过程:从左边开始,每遇到和目标状态不同的硬币就操作一步,翻动连续两个硬币,直到最后一个硬币。这是贪心法,但是这题用贪心对吗?下面进行分析和证明。

首先分析翻动的具体操作:

1)只有一个硬币不同。例如位置 a 的硬币不同,那么翻动它时,会改变它相邻的硬币 b,现在变成了硬币 b 不一样,回到了“只有一个硬币不同”的情况。也就是说,如果只有一个硬币不同,无法实现。

2)有两个硬币不同。这两个硬币位于任意两个位置,从左边的不同硬币开始翻动,一直翻动到右边的不同硬币,结束。

3)有三个硬币不同。左边两个不同硬币,可以用操作(2)完成翻动;但是最后一个硬币是操作(1),无法完成。

总结这些操作,得到结论:

1)有解的条件初始字符串 s 和目标字符串 t 必定有偶数个字符不同。

2)贪心操作从头开始遍历字符串,遇到不同的字符就翻动,直到最后一个字符。

下面证明这个贪心操作,是局部最优也是全局最优。

首先找到第一个不同的字符。从左边开始找第一个不同的那个字符(记为 Z),Z 左边的字符都相同,不用再翻动。从 Z 开始,右边肯定有偶数个不同的字符。Z 必定要翻动,不能不翻;它翻了之后,就不用再翻动。所以从左到右的翻动过程,每次翻动都是必须的,也就是说这个翻动 Z 的局部最优操作,也是全局最优操作。贪心是正确的。

暴力法

第 6 行和第 7 行翻动连续 2 个字符。其实第 6 行是多余的,因为当遇到一个不同的字符 s[i] 时,翻它的下一个字符 s[i+1] 就行了,不用再管 s[i]。

s=list(input())
t=list(input())
ans=0
for i in range(len(s)-1):
    if s[i]!=t[i]:
        #s[i]='*' if s[i]=='o' else 'o'     #多余
        s[i+1]='*' if s[i+1]=='o' else 'o'  #三目运算
        ans+=1
print(ans)

2、防御力(lanqiaoOJ题号226,2018年国赛)

【题目描述】

小明最近在玩一款游戏。对游戏中的防御力很感兴趣。直接影响防御的参数为"防御性能",记作 d,而面板上有两个防御值 A 和 B ,与 d 成对数关系, A=2d,B=3d (注意任何时候上式都成立) 。在游戏过程中,可能有一些道具把防御值 A 增加一个值,有另一些道具把防御值 B 增加一个值。现在小明身上有 n1 个道具增加 A 的值和 n2 个道具增加 B 的值,增加量已知。现在已知第 i 次使用的道具是增加 A 还是增加 B 的值,但具体使用哪个道具是不确定的,请找到一个字典序最小的使用道具的方式,使得最终的防御性能最大。初始时防御性能为 0,即 d=0,所以 A=B=1。

【输入格式】

输入的第一行包含两个数 n1, n2,空格分隔。第二行 n1 个数,表示增加 A 值的那些道具的增加量。第三行 n2 个数,表示增加 B 值的那些道具的增加量。第四行一个长度为 n1+n2 的字符串,由 0 和 1 组成,表示道具的使用顺序。0 表示使用增加 A 值的道具,1 表示使用增加 B 值的道具。输入数据保证恰好有 n1 个 0, n2 个 1。

【输出格式】

对于每组数据,输出 n1+n2+1 行,前 n1+n2 行按顺序输出道具的使用情况,若使用增加 A 值的道具,输出Ax,x 为道具在该类道具中的编号 (从1开始)。若使用增加 B 值的道具则输出 Bx。最后一行输出一个大写字母 E。

【测试】

对于 20% 的数据,字符串长度 <=10000;

对于 70% 的数据,字符串长度 <=200000;

对于 100% 的数据,字符串长度 <=2000000,输入的每个增加值不超过2^30。

【输入样例】

1 2

4

2 8

101

【输出样例】

B2

A1

B1

E

(1)贪心法编程

  • 对 Ai 进行结构体排序,先对 Ai 按增加量的从小到大排序,再按下标 (字典序) 排序。
  • 对 Bi 进行结构体排序,先对 Bi 按增加量的从大到小排序,再按下标 (字典序) 排序。
  • 然后按题目要求的顺序,输出 Ai 和 Bi

分析过程复杂,代码很简单。这一题是蓝桥杯国赛题,分析过程有一点复杂,不过难度不高,参加国赛的大部分人能做出来。

  • 复杂度:很好
  • 主要是排序花时间:O(nlogn)
def cmp(x):
    return x[1]

n1,n2=map(int,input().split())
a=list(map(int,input().split()))
b=list(map(int,input().split()))

for i in range(n1):
    a[i]=(i+1,a[i])
for i in range(n2):
    b[i]=(i+1,b[i])
a.sort(key=cmp)
b.sort(key=cmp,reverse=True)
s=input()
idx1,idx2=0,0
for i in range(n1+n2):
    if s[i]=='1':
        print("B%d"%b[idx1][0])
        idx1+=1
    else:
        print("A%d"%a[idx2][0])
        idx2+=1
print("E")

以上,贪心法讲解

祝好

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/159589.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构:线性表的类型定义

线性表的类型定义 线性表是最简单、也是最基本的一种线性数据结构。它有两种存储表示方法&#xff1a;顺序表和链表&#xff0c;它的主要基本操作是插入、删除和查找。 线性表是n(n>0)个数据元素的有限序列&#xff0c;表中各个数据元素具有相同特性&#xff0c;即属同一数…

LeetCode 114. 二叉树展开为链表

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 114. 二叉树展开为链表&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 Leet…

2021年大数据挑战赛A题智能运维中的异常检测与趋势预测

2021年大数据挑战赛 A题 智能运维中的异常检测与趋势预测 原题再现&#xff1a; 异常检测&#xff08;异常诊断/发现&#xff09;、异常预测、趋势预测&#xff0c;是智能运维中首当其冲需要解决的问题。这类问题是通过业务、系统、产品直接关联的 KPI 业务指标进行分析诊断&…

MD语法 官方参考

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

Python小技巧:if __name__ == “__main__“ 的作用

前言 这里是Python小技巧的系列文章。这是第一篇&#xff0c;if __name__ "__main__" 的作用。 在编写Python程序时候&#xff0c;总是习惯性的在文件的末尾添加这么一段代码。 if __name__ "__main__":...至于它的作用是什么&#xff0c;先不管&#x…

内网渗透-src挖掘-外网打点到内网渗透-3层内网渗透测试记录(2)-2023年1月

1、确定目标之后&#xff0c;先进行信息搜集 对目标的先进行ip端口扫描&#xff0c;发现开放了22和80端口 2、访问80端口,为Laravel框架 3、发现该版本为Laravel v8.78.1 (PHP v7.4.3) 漏洞库中搜索&#xff0c;发现该版本的Laravel存在&#xff0c;CVE-2021-3129-Laravel De…

免费格式转换器有哪些?这几款一定要试试

相信在现在的工作中&#xff0c;大家都或多或少需要处理一些文件&#xff0c;有时候我们需要把PDF文件转为word文件&#xff0c;还可能需要将PDF文件转为PPT文件等等&#xff0c;这个时候我们就需要选择一款好用的格式转换器&#xff0c;如果转换文件量比较大的小伙伴们还需要重…

odoo16 修改odoo加载过程

在odoo加载过程中&#xff0c;title处会显示odoo以及odoo标签图片&#xff0c; 所以我们可以进行修改&#xff0c;将其公司化 1. 修改无公司名称时显示字样 <title t-esc"title or Odoo"/> <!--路径&#xff1a;addons/web/views/webclient_templates.xml…

2022.12 青少年软件编程(Python) 等级考试试卷(一级)

2022年12月 青少年软件编程&#xff08;Python&#xff09; 等级考试试卷&#xff08;一级&#xff09; 分数&#xff1a; 100 题数&#xff1a; 37 一、 单选题(共 25 题&#xff0c; 共 50 分) 1. 关于Python语言的注释&#xff0c;以下选项中描述错误的是&#xff1f;&#…

ThinkpadCPU锁频0.4G问题

1.问题描述 Thinkpad型号&#xff1a;E15 CPU&#xff1a;i5-10210U1.6GHz 系统&#xff1a;Win10家庭版 ThinkpadCPU锁频0.4G&#xff0c;具体是过程是今天我需要去楼下打印东西&#xff0c;要带着笔记本电脑&#xff0c;拔掉电源我就去楼下。 刚开始还是好好的&#xff0c;…

PyQt5 serial模块-串口小工具

PyQt5 serial模块-串口小工具串口简述serial模块参数方法使用模板串口工具示例串口简述 异步串行是指UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;&#xff0c;通用异步接收/发送。UART是一个并行输入成为串行输出的芯片&#xff0c;通常集成在主…

java 基础练习 圆的面积

问题描述给定圆的半径r&#xff0c;求圆的面积。输入格式输入包含一个整数r&#xff0c;表示圆的半径。输出格式输出一行&#xff0c;包含一个实数&#xff0c;四舍五入保留小数点后7位&#xff0c;表示圆的面积。说明&#xff1a;在本题中&#xff0c;输入是一个整数&#xff…

Oracle通过数据库链连接KingbaseES

测试环境&#xff1a; ip数据库版本192.168.254.135oracle 11g192.168.254.137V008R006C005B0023 通过oracle官网得知&#xff0c;Oracle使用DG4ODBC作为连接到其他非Oracle数据库的数据网关。其原理图如下&#xff1a; 所以我们需要在Oracle服务器安装unixODBC和KingbaseES的O…

4 位硬科技创始人对谈 DevOps 硬件实战

随着 DevOps 技术更为广泛地在各行各业应用&#xff0c;从公司层面来看&#xff0c;考察的不仅仅是 DevOps 平台的技术&#xff0c;更多的是 DevOps 平台与企业产品的兼容性与企业文化能否为 DevOps 提供落地的土壤&#xff0c;这背后是商业模式、思维方式、管理理念的一系列变…

【项目实战】一文讲解车企是如何做HTTPS双向认证的

一、背景 很多主机厂都说需要做HTTPS的双向认证&#xff0c;那到底什么是双向认证呢&#xff1f; 二、什么是Https的双向认证&#xff1f; 英文是Mutual TLS HTTPS双向认证是一种安全通信方式&#xff0c;它需要在客户端和服务端之间进行双向身份验证。在这种方式中&#x…

2.浅谈计算机系统(硬件+软件)。

一、现代计算机系统是由哪两部分组成的&#xff1f;1.什么是物联网&#xff1f;2.有些计算机是非常复杂的。二、计算机系统简介。计算机系统的构成&#xff08;硬件、软件&#xff09;。三、软件。1.系统软件。2.应用软件。思维导图是清菡本人所画。一、现代计算机系统是由哪两…

C#上位机基础学习_基于SOCKET实现与PLC服务器的TCP通信(一)

C#上位机基础学习_基于SOCKET实现与PLC服务器的TCP通信(一) 测试软件: TIA PORTAL V15.1 S7-PLCSIM ADVANCED V3.0 Visual Studio 2019 如下图所示,打开S7-PLCSIM ADVANCED V3.0仿真软件,新键一个实例,设置仿真PLC的IP地址等参数,然后点击Start激活PLC, 如下图所示,…

蓝牙耳机怎么连接电脑?无线耳机连接电脑的教程

想要减少噪音&#xff0c;更好地聆听音乐&#xff0c;或者是看电视&#xff0c;很多人都会选择戴耳机。其中蓝牙耳机成为越来越多人的选择&#xff0c;它方便携带&#xff0c;没有有线的缠绕。 可能有人会问&#xff1a;无线蓝牙耳机可以连接电脑吗&#xff1f;当然可以。可是…

【3 - 数据预处理】菜菜sklearn机器学习

课程地址&#xff1a;《菜菜的机器学习sklearn课堂》_哔哩哔哩_bilibili 第一期&#xff1a;sklearn入门 & 决策树在sklearn中的实现第二期&#xff1a;随机森林在sklearn中的实现第三期&#xff1a;sklearn中的数据预处理和特征工程第四期&#xff1a;sklearn中的降维算法…

前端使用xlsx-js-style导出Excel文件并修饰单元格样式

xlsx-js-style安装引入需要导出的数据源将数据源转成需要的二维数组定义 Excel 表头将定义好的表头添加到 body 中将二维数组转成 sheet!merges 设置单元格合并!cols 设置列宽!rows 设置行高创建虚拟的 workbook向 workbook 中添加 sheet导出 workbook完整示例效果图总结安装 …