算法基础部分6-贪心算法

article/2025/10/4 19:26:44

算法部分 基础6

一、贪心算法简述

  贪心算法的每一步行动总是按照某种指标选取最优的操作来进行该指标,只看眼前并不考虑以后可能造成的影响。证明方法通过替换法和数学归纳法实现。

二、贪心算法例子

1. 圣诞老人的礼物

  问题描述:圣诞节来临了,中圣诞老人准备分发糖果,现在有多箱不同的糖果,每箱糖果都有自己的价值和重量,每箱糖果都可以拆分成任意散装组合带走。圣诞老人的驯鹿雪橇最多只能装下重量为 W 的糖果,请问圣诞老人最多能带走多大价值的糖果。

输入:第一行由两个部分组成,分别为糖果箱数正整数 n (1 <= n <= 100), 
驯鹿能承受的最大重量正整数 w (0 < w < 10000), 两个数用空格隔开。其余
n 行每行对应一箱糖果,由两部分组成,分别为一箱糖果的价值正整数 v 和重量
正整数 w , 中间用空格隔开。
输出:输出为圣诞老人能带走的糖果的最大总价值,保留 1 位小数。输出为一
行,以换行符结束。样例输入
输入:
4 15
100 4
412 8
266 7
591 2输出:
1193.0

  分析和程序如下

按礼物的价值/重量比从大到小一次选取礼物,对选取的礼物尽可能地多装,直到
重量为 w , 复杂度 O(nlogn).程序如下:
#include <stdio.h>
#include <iostream>
#include <algorithm>using namespace std;const double eps = 1e-6;
struct Candy{int v; int w;
}candies[110];//箱子按单位重量价值排序
bool operator < (const Candy & a, const Candy & b) {return float(a.v) / a.w - float(b.v) / b.w > eps;
}int main(){int n, w;scanf("%d%d", & n, & w);for(int i = 0; i < n; i++){scanf("%d%d", & candies[i].v, & candies[i].w);}sort(candies, candies + n);int totalW = 0;double totalV = 0;// 排好序,按照顺序取就行for(int i = 0; i < n; i++){if(totalW + candies[i].w <= w){totalW += candies[i].w;totalV += candies[i].v;}else{totalV += candies[i].v *double(w - totalW) / candies[i].w;break;}}printf("%.1f", totalV);return 0;
}

  结果如下,
在这里插入图片描述
  对于这个题,若糖果只能整箱拿,则贪心法得到结果是错误的。考虑以下例子,只能整箱拿, 3 个箱子 (8, 6), (5, 5), (5, 5), 雪橇总容量为 10 。如果按照贪心算法,拿的是 (8, 6) ,就不能拿了。但是 (5, 5), (5, 5) 是最优的。

2. 电影节

  问题描述:大学生电影节在北大举办!这天,在北大各地放了多部电影,给定每部电影的放映时间区间,区间重叠的电影不可能同时看(端点可以重合),问李雷最多可以看多少部电影。

输入:多组数据。每组数据开头都是 n (n <= 100), 表示共 n 场电影,接下
来 n 行,每行两个整数 (均是小于 1000) , 表示一场电影的放映区间,n = 0
则数据结束。
输出:对每组数据输出最多能看几部电影样例如下
输入:
12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9输出:
5

  分析

贪心解法:将所有电影按结束时间从小到大排序,第一步结束时间最早的那部电影。然
后,每步都选和上一部选中的电影不冲突且结束时间最早的电影。复杂度:O(nlogn)

  程序如下

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>struct Film{int starts;int ends;
}a[100];
// 使用快速排序需要一种比较方式
// void * 类型指针可以强制转化为任何类型指针
int cmp(const void * a,const void * b){struct Film * pa = (struct Film *) a;struct Film * pb = (struct Film *) b;if( pa -> ends >= pb -> ends) return 1;else return -1;
}
int main(){int n;int numFilm;int endTime;scanf("%d", & n);while(n != 0){int i = 0;for(i = 0; i < n; i++){scanf("%d%d", & a[i].starts, & a[i].ends);}// 使用快排,用 cmp 函数作为比较方式qsort(a, n, sizeof(a[0]), cmp);numFilm = 1;endTime = a[0].ends;for(i = 1; i < n; i++){if(a[i].starts >= endTime){numFilm ++;endTime = a[i].ends;}}printf("Out: %d\n", numFilm);}return 0;
}

  结果如下
在这里插入图片描述
  这个也比较好理解,通过对电影放映结束时间进行排序,这样的策略来实现贪心算法。

3. 分配畜栏

  有 n 头牛 (1 <= n <= 50, 000) 要挤奶。给定每头牛挤奶的时间区间 [A, B] (1 <= A <= B <= 1, 000, 000,A,B 为整数) 。
  牛需要待蓄栏同一时间只能容纳一头牛。问至少需要多少个蓄栏,才能完成全程挤奶工作,以及每头牛都放在哪个蓄栏里。
  去同一个蓄栏的两头牛,它们挤奶时间区间哪怕只在端点重合也是不可以的。
  分析

所有奶牛都必须挤奶。到了一个奶牛的挤奶开始时间,就必须为这个奶牛找畜栏。因此按照奶牛
的开始时间逐个处理它们,是必然的。S(x) 表示奶牛 x 的开始时间。E(x) 表示 x 的结束时间。对 E(x), x 可以是奶牛,也可
以是畜栏。畜栏的结束时间,就是正在其里面挤奶的奶牛的结束时间。同一个畜栏的结束时间是
不断在变的。1) 把所有奶牛按开始时间从小到大排序。
2) 为第一头奶牛分配一个畜栏。
3) 依次处理后面每头奶牛 i 。处理 i 时,考虑已分配畜栏中,结束时间最早的畜栏 x .E(x) < S(i) , 则不用分配新畜栏,i 可进入 x , 并修改 E(x)E(i)E(x) >= S(i) , 则分配新畜栏 y ,记 E(y) = E(i)
直到所有奶牛结束需要用优先队列存放已经分配的畜栏,并使得结束时间最早的畜栏始终位于队列头部。复杂度 O(nlogn)

  程序如下,

#include <stdlib.h>
#include <algorithm>
#include <queue>
#include <vector>using namespace std;struct Cow{int startT, endT; // 挤奶区间, 开始时间和结束时间int No; // 编号// 定义结构体比较大小的方式bool operator < (const Cow & c) const {return startT < c.startT;}
} cows[50100];
int pos[50100]; // pos[i] 表示标号为 i 的奶牛去的畜栏编号// 畜栏结构体
struct Stall{int ends;   // 结束时间int No;     // 编号bool operator < (const Stall & s) const {return ends > s.ends;}Stall(int e, int n): ends(e), No(n){ }
};int main(){int n;scanf("%d", & n);for(int i = 0; i < n; i++){scanf("%d%d", & cows[i].startT, & cows[i].endT);cows[i].No = i;}sort(cows, cows + n);int total = 0;// 在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。priority_queue <Stall> pq;for(int i = 0; i < n; i++){// 队列代表的是畜栏的个数,FIFOif( pq.empty() ){total++;pq.push(Stall(cows[i].endT, total));pos[cows[i].No] = total; // 某个牛被安排在第 total 的畜栏}else{// 返回 结束时间 最大的畜栏Stall st = pq.top(); // 优先队列能够把最大的放在最上面if(st.ends < cows[i].startT){pq.pop();pos[cows[i].No] = st.No;pq.push(Stall(cows[i].endT, st.No));}else{ // st.ends > cows[i].startT// 开辟新的畜栏total++;pq.push(Stall(cows[i].endT, total));pos[cows[i].No] = total;}}}printf("Stall num: %d\n", total);for(int i = 0; i < n; i++){printf("Cow %d in %d stall.\n", i, pos[i]);}return 0;
}

运行结果如下,
在这里插入图片描述
程序分析,

1. 
定义两个结构体:
struct Cow    定义了每头牛开始挤奶时间和结束时间,并对牛编号
struct Stall  定义了每个畜栏中编号为 i 的牛结束的时间,并且该牛占用到时间结束
并且两个结构体都对运算符 < 进行了重载,方便比较。2. 
// 注意 Cow 对小于号的重载,是对结束时间进行比较
sort(cows, cows + n); // 使用排序的方法,对牛按照结束时间从小到大排序3. 
// 注意 Stall 对小于号重载,是对结束时间进行比较,所以结束时间最短的在队头
// 注意,是对 < 重载, return ends > s.ends; 返回是大于号,结束时间最短的在队头
priority_queue <Stall> pq; // 使用优先队列,让结束时间最短的在队头。4.
// 返回 结束时间 最小的畜栏,Stall 对运算符进行了重载
Stall st = pq.top(); // 优先队列能够把优先级最大的放在最上面
// 如果最小的完成了,那新的牛进入队列
if(st.ends < cows[i].startT){pq.pop();// 返回已有畜栏的编号pos[cows[i].No] = st.No;pq.push(Stall(cows[i].endT, st.No));
}else{ // st.ends > cows[i].startT// 开辟新的畜栏total++;pq.push(Stall(cows[i].endT, total));pos[cows[i].No] = total;
}

  这个程序难点和要点是对优先队列的使用和运算符重载,使用的是贪心算法,这个题还是比较有意思的。

4. 安装雷达

  x 轴是海岸线,x 轴上方是海洋。海洋中有 n (1 <= n <= 1000) 个岛屿,可以看作点。
  给定每个岛屿的坐标 (x, y) , x, y 都是整数。
  当一个雷达 (可以看作点) 到岛屿的距离不超多 d (整数), 则认为该雷达覆盖了该岛屿。
  雷达只能放在 x 轴上。问至少需要多少个雷达才可以覆盖全部岛屿。
  如图,
在这里插入图片描述
  分析如下,

对于每个岛屿 P , 可以算出,覆盖它的雷达,必须位于 x 轴上的区间 [Ps, Pe].
如果有雷达位于某个 x 轴区间 [a, b], 称该雷达覆盖此区间。问题转换为,至少要在 x 轴
上放几个雷达 () , 才能覆盖全部区间 [P1s, P1e], [P2s, P2e] ... [Pns, Pne].重要结论:
如果可以找到一个雷达同时覆盖多个区间,那么把这多个区间按起点坐标从小到大排序,则最后
一个区间(起点靠右的) k 的起点,就能覆盖所有区间。
所以只用找区间的起点来放置雷达

  输出方式

输入:输入包含若干组数据,每组数据的第一行是两个整数 n 和 d (1 <= n <= 1000), 分
别表示小岛的数量和雷达的半径。接下来有 n 行, 每行的两个整数分别表示各个小岛的坐标。
输入以 0 0 结束。
输出:对于每组数据输出一行,为最少的雷达数,如果该组数据无解,则输出 -1 .输出样例
输入:
3   2
1   2
-3  1
2   1
1   2
0   2
0   0
输出:
case 1: 2
case 2: 1

  程序如下

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>using namespace std;#define MAXN 1010// 雷达在[Psi, Pei] 可以探测到改点
struct Node{double left, right;
};int T, n, d;
Node s[MAXN];// 比较左边节点,找到最后一个满足可以覆盖前面一组的点
bool operator < (const Node & a, const Node & b){return a.left < b.left;
}// 求解每个 case
int Solve(){int ans = 1; // 雷达个数double now;  // 标记能够覆盖的点// 按左端点排序sort(s, s + n);// 若当前线段与目前集合中的线段没有交集,则加入一个新雷达now = s[0].right;for(int i = 1; i < n; i++){if(s[i].left <= now) now = min(now, s[i].right);else{ans++;now = s[i].right;}}return ans;
}int main(){int x, y;bool flag;T = 0;while(true){T++;// 雷达总数和雷达半径scanf("%d%d", & n, & d);if(n == 0 && d == 0) break;// 读入flag = true;for(int i = 0; i < n; i++){// x, y 是坐标scanf("%d%d", & x, & y);if(y > d) flag = false;else{// 勾股定理计算该点雷达可以检测两端点s[i].left = x - sqrt(d * d - y * y);s[i].right = x + sqrt(d * d - y * y);}}// 若有小岛距离 x 轴大于 d , 则输出 -1if(flag) printf("Case %d: %d\n", T, Solve());else printf("Case %d: -1\n", T);}return 0;
}

结果如下
在这里插入图片描述
  这个程序比较好理解,主要是上面分析过程,关于数学逻辑和推导。感觉贪心算法主要是考虑数学逻辑和对问题分析能力。

三、总结

  现在程序设计与算法部分一,二,三全部刷完了。现在开始正式入门 C++ ,后面算法会继续刷着,我觉得精彩,难理解的会在博客分享。后面应该记录周期会长一些了。不过依旧会坚持更新,深入深入!


http://chatgpt.dhexx.cn/article/PY7wjf7C.shtml

相关文章

python外包_python怎么找外包

很多程序员兄弟们工资不高&#xff0c;但时间还是比较充足的&#xff0c;剩下的时间想多搞点外快&#xff0c;毕竟要养家糊口。我来讲讲怎么做可以一个月轻松多赚1万多元的外快。 一、接项目 是程序员都想过接私活&#xff0c;但真正去做了的寥寥无几。可能的原因&#xff1a;…

国外最流行的几个外包接活网站 简要介绍

国外主要外包接活站点&#xff1a; No.1 Freelancer.com &#xff08;旧称GAF – GetAFreelancer&#xff09;: 国外访问人数最多&#xff0c;更新速度最快的外包平台。详细介绍 No.2 Elance : 国外比较成熟的三大业务外包平台之一&#xff0c;外包项目类型以软件和网站为主。当…

PHP学习笔记

学习笔记 PHP学习笔记部署遇到的问题一二 php在win10 64位下执行curl_init()方法报错的bugthinkphp登陆失效的问题不同的win系统上无法使用curl的问题thinkphp下调用python脚本先要有反应提示python不是内部命令以上处理后还提示no file /directory 关于json里为数组时出现的问…

PHP实现支付宝支付

支付宝付款,开发上比起微信支付要简单很多,今天就以支付宝手机网站支付为例,简单讲一下实现方法: 前期准备,当然就不多说了,当你想开发使用支付宝支付,必然需要在支付宝开放品台注册认证并且创建好应用并且具备手机网站支付功能!不明白可以查看支付宝官方文档(https://docs.op…

外包网站建设需要注意什么

外包网站建设需要注意什么 对于如今许多的公司来说&#xff0c;拥有一个属于公司的网站是非常必要的。但是并不是所有的公司都会拥有专业的人员来进行网站的建设&#xff0c;所以网站建设外包则成为了诸多公司的首选。但是外包网站建设都需要哪些问题呢&#xff1f; 外包网站…

苏州外包php,【苏州IT外包经验】Ubuntu 17.10系统下配置PHP+Apache+Mysql

发布于 2017年11月16日 星期四 01:15 点击数&#xff1a;20883 本文为Ubuntu 17.10系统下配置PHP7.1Apache2.4Mysql5.7 一、Apache2 安装apache sudo apt-get apache2 编辑apache主配置文件/etc/apache2/apache2.conf&#xff0c;修改KeepAlive设置 KeepAlive Off Apache默认的…

Linux任务之自动发邮件

Linux任务之自动发邮件 文章目录 Linux任务之自动发邮件1、虚拟机Centos7环境&#xff08;1&#xff09;先设置自己的邮箱&#xff0c;我这用qq邮箱&#xff1a;&#xff08;2&#xff09;获取到密码后&#xff0c;我们要在Linux上配置邮件文件&#xff1a;&#xff08;3&#…

Mac 定时自动发送邮件

文章目录 WhyHow1. 在automator中创建application2. 创建calender 自动定时发送email3. 自行测试 Refer Why 工作中&#xff0c;有时需要发送固定格式的email&#xff0c;每次人工做&#xff0c;比较繁琐&#xff0c;本篇博客描述了一种可以自动定时发送email的方式。 下面例…

如何利用 Python 自动发邮件,打工人福音

在工作中&#xff0c;每天或者每周结束的时候我们都会发送相应的日报或者周报给上级领导&#xff0c;来汇报你做了那些工作&#xff0c;可是汇报工作内容的时候我们始终都是打开邮箱、写入内容&#xff0c;发送和抄送给固定的人&#xff0c;那么这么繁琐并且重复的一件事&#…

用Excel和OutLook实现自动批量发邮件

转自&#xff1a;https://zhuanlan.zhihu.com/p/25283201 作者&#xff1a;闲者秋山 闲者秋山 闲者秋山 相信不少人的工作中都有这样的情况&#xff0c;需要群发通知邮件的情况。 &#xff08;比如财务付完款&#xff0c;要通知供应商&#xff0c;房东之类的&#xff09; 但如…

JAVA实现自动发送邮件

JAVA实现发送邮件 做项目时用到的小工具 做个备份&#xff08; tips: 需要导入javax.mail 我使用了maven导入X <!-- JavaMail相关依赖 --><dependency><groupId>javax.mail</groupId><artifactId>javax.mail-api</artifactId><ver…

自动发送

自动发送 主要用于普通邮箱&#xff08;个人邮箱/企业邮箱&#xff09;群发&#xff0c;用户使用“群发单显”或“精准分发”生成邮件在“待发邮件”邮件夹中&#xff0c;开启自动发送&#xff0c;可按照设定的时间和速度进行邮件的自动发送。 最新功能&#xff08;版本&#x…

python---自动群发邮件

生活中我们经常发送邮件&#xff0c;那么我们能不能用Python写一个自动发送邮件的功能呢&#xff1f;答案是肯定的&#xff01;&#xff01;&#xff01; 开始实现功能之前我们需要开启我们邮箱的 IMAP/SMTP功能&#xff0c;我们先了解一下什么是IMAP/SMTP。 SMTP 的全称是“…

Python之邮箱自动发件

在使用python给好友发送邮件时&#xff0c;首先需要下载python中在对模块&#xff0c;以及打开邮箱IMAP/SMTP服务。接下来一步一步介绍读者如何来使用并实现发件。 1.下载python中在对模块 首先键盘点击winR键&#xff0c;输入pip search smtplib便可以看到smtplib模块下含有…

使用Python自动发送邮件

发送普通邮件 SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;是简单传输协议。 python中对SMTP进行了简单的封装&#xff0c;可以发送纯文本邮件、HTML 邮件以及带附件的邮件。两个核心模块如下&#xff1a; email模块&#xff1a;负责构建邮件 smtplib模块&am…

通过SMTP协议自动发送邮件

通过SMTP协议实现训练结束自动发送邮件 为了更安心地摸鱼&#xff0c;想让大模型训练完之后自己发邮件给我。 开启SMTP服务与取得授权码 1.打开设置->账户->打开服务 然后会获得授权码 代码示例 #!/usr/bin/python\# -*- coding: utf-8 -*-import smtplib from e…

Java实现邮件自动发送

有时候我们会遇到自动发送邮件通知、邮件验证码、节假日祝福邮件发送、邮件自动发送广告功能&#xff0c;今天我们就谈谈邮件自动发送是怎么实现的。案例使用Java语言来编写&#xff0c;使用springBoot来搭建项目。 步骤一&#xff1a;添加邮件依赖&#xff1a;spring-boot-st…

sql自动发邮件功能

用SQL自动发邮件需要三步 1&#xff0c;做一个配置文件 2&#xff0c;调用存储过程msdb.dbo.sp_send_dbmail 3, 创建作业 1&#xff0c; 右键->【配置数据库邮件】 配置好了之后可以测试能否发送邮件 进去邮箱查看是否收到邮件或者数据库查询 select * from msdb.dbo.sys…

自动定时发送邮件的方法 —— 定时执行专家V6.8

目录 ▉ 定时发送简单邮件 ◆前提条件 ◆实现思路 ◆设置方法 ▉ 定时执行专家简介 ◆最新版下载链接 ◆2023-03-21 V6.8 - 更新日志 ▉ 定时发送简单邮件 ◆前提条件 - 电脑安装邮件客户端&#xff0c;使用这个邮件客户端&#xff0c;发送简单邮件。 ◆实现思路 组…

自动化发送邮件

自动化发送邮件 自动化发送邮件 背景SMTP了解一下邮件系统实现过程 1.连接建立2.邮件传输3.连接释放4.注意项目 信息格式MIME新增关键字multipart自动发邮件 构建过程 封装代码必要的设置 背景 室友选修的《机器学习》&#xff0c;结课作业是对8万的数据进行分析。他向我抱怨…