当前位置: 首页 > news >正文

贪心算法:基础入门篇

贪心算法:基础入门篇

文章目录:

  • 贪心算法:基础入门篇
    • 一、认识贪心算法
    • 二、常见贪心问题
      • 2.1 纸牌问题
      • 2.2 背包问题(基础版)
      • 2.3 简单数学证明问题
    • 三、总结

一、认识贪心算法

在求最优解的问题中,以某种贪心标准,从状态的最初始找到每一步最优解,通过多次的贪心求解,最终得到整个问题的最优解,此种解题的方法为贪心算法。可见贪心算法并不是一种固定的算法,而是根据问题的条件而产生的一种解决问题的思维模式。

由定义可知,贪心算法是由局部的最优解,得到总体的最优解,因此在使用贪心算法之前,要先判断问题是否适合使用贪心算法。

二、常见贪心问题

2.1 纸牌问题

纸牌问题是最简单,也最好理解的贪心问题,题目如下:

题目描述:

N N N 堆纸牌,编号分别为 1 , 2 , … , N 1,2,\ldots,N 1,2,,N。每堆上有若干张,但纸牌总数必为 N N N 的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为 1 1 1 堆上取的纸牌,只能移到编号为 2 2 2 的堆上;在编号为 N N N 的堆上取的纸牌,只能移到编号为 N − 1 N-1 N1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如 N = 4 N=4 N=4 时, 4 4 4 堆纸牌数分别为 9 , 8 , 17 , 6 9,8,17,6 9,8,17,6

移动 3 3 3 次可达到目的:

  • 从第三堆取 4 4 4 张牌放到第四堆,此时每堆纸牌数分别为 9 , 8 , 13 , 10 9,8,13,10 9,8,13,10
  • 从第三堆取 3 3 3 张牌放到第二堆,此时每堆纸牌数分别为 9 , 11 , 10 , 10 9,11,10,10 9,11,10,10
  • 从第二堆取 1 1 1 张牌放到第一堆,此时每堆纸牌数分别为 10 , 10 , 10 , 10 10,10,10,10 10,10,10,10

输入格式:

第一行共一个整数 N N N,表示纸牌堆数。
第二行共 N N N 个整数 A 1 , A 2 , … , A N A_1,A_2,\ldots,A_N A1,A2,,AN,表示每堆纸牌初始时的纸牌数。

输出格式:

共一行,即所有堆均达到相等时的最少移动次数。

样例:

样例输入

4
9 8 17 6

样例输出

3

提示

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 100 1 \le N \le 100 1N100 1 ≤ A i ≤ 10000 1 \le A_i \le 10000 1Ai10000

【题目来源】

NOIP 2002 提高组第一题


题目解析:

根据移牌规则,可以得出以下通式:
{ a i > a v e , a i + 1 = ( a i − a v e ) + a i + 1 a i = a v e a i < a v e , a i + 1 = a i + 1 − ( a v e − a i ) \left\{\begin{matrix}a_i> ave,a_{i+1}=(a_i-ave)+a_{i+1}\\a_i=ave \\a_i< ave,a_{i+1}=a_{i+1}-(ave-a_i)\end{matrix}\right. ai>ave,ai+1=(aiave)+ai+1ai=aveai<ave,ai+1=ai+1(aveai)
在左至右开始排列的情况下,从移牌规则中可得知,大于平均数的牌只能向左移动,反之小于平均数的牌只能向右移动。由此可以画出以下关系图:

移动
移动
移动
移动
第一堆
第二堆
第N堆
根据上述思路即可写出相应代码:
#include <iostream>
using namespace std;
int main(){int a,sum=0,ave=0;int b[100];cin>>a;for(int i=0;i<a;i++){cin>>b[i];ave+=b[i];}ave=ave/a;for(int i=0;i<a;i++){if(b[i]>ave){b[i+1]+=b[i]-ave;sum++;}if(ave>b[i]){b[i+1]=b[i+1]-(ave-b[i]);sum++;}}cout<<sum;return 0;
}


2.2 背包问题(基础版)

背包问题是贪心算法中比较经典的贪心问题,同时背包问题也是一个经典的动态规划问题,其基本形式是:给定一个背包,容量为C,和一组物品,每个物品有自己的重量和价值,现在从这些物品中选择一些装入背包,要求背包中物品的总重量不超过C,且总价值最大。

接下来以一个题目来进行讲解:
题目描述

阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N ( N ≤ 100 ) N(N \le 100) N(N100) 堆金币,第 i i i 堆金币的总重量和总价值分别是 m i , v i ( 1 ≤ m i , v i ≤ 100 ) m_i,v_i(1\le m_i,v_i \le 100) mi,vi(1mi,vi100)。阿里巴巴有一个承重量为 T ( T ≤ 1000 ) T(T \le 1000) T(T1000) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?

输入格式

第一行两个整数 N , T N,T N,T

接下来 N N N 行,每行两个整数 m i , v i m_i,v_i mi,vi

输出格式

一个实数表示答案,输出两位小数

样例

样例输入

4 50
10 60
20 100
30 120
15 45

样例输出 #1

240.00

【题目来源】

洛谷 P2240 【深基12.例1】部分背包问题


题目解析:

根据题目可知,金币堆可以拆分,并将价值最大的情况带走。对于此题,我们需求出每堆金币的单价,找出单价最高的,然后依次搜索,到背包装满。

Created with Raphaël 2.3.0 开始 找出单价最高的金币堆 装走金币 背包装满? 结束 yes no

根据此流程图可写出代码:

#include <iostream>
#include <iomanip>
using namespace std;
int main() {int n, t;float money=0;float a[100][3];cin >> n >> t;for (int i = 0; i < n; i++)for (int j = 0; j < 2; j++) {cin >> a[i][j];a[i][2] = a[i][1] / a[i][0];}
to:float max = 0;int max_num;for (int i = 0; i < n; i++) {if (max < a[i][2]) {max = a[i][2];max_num = i;}}if (t <= a[max_num][0]) {money += t * max;}else {money += a[max_num][0] * max;t = t - a[max_num][0];a[max_num][2] = 0;goto to;}cout << fixed << setprecision(2) << money;return 0;
}


2.3 简单数学证明问题

在数学问题中,存在多分支的情况下,不知道最优解时,可用贪心算法找出最优解。
题目描述

n n n 个人在一个水龙头前排队接水,假如每个人接水的时间为 T i T_i Ti,请编程找出这 n n n 个人排队的一种顺序,使得 n n n 个人的平均等待时间最小。

输入格式

第一行为一个整数 n n n

第二行 n n n 个整数,第 i i i 个整数 T i T_i Ti 表示第 i i i 个人的等待时间 T i T_i Ti

输出格式

输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

样例

样例输入

10 
56 12 1 99 1000 234 33 55 99 812

样例输出

3 2 7 8 1 4 9 6 10 5
291.90

提示

1 ≤ n ≤ 1000 1\le n \leq 1000 1n1000 1 ≤ t i ≤ 1 0 6 1\le t_i \leq 10^6 1ti106,不保证 t i t_i ti 不重复。

【题目来源】

洛谷 P1223 排队接水


题目解析:
根据题目可知道,要让平均等待时长最少,就需要将总等待时长控制在最小,因此需要将用时最短的人,最先安排接水,就可以得到最短总时长。数学表达式如下:
f m i n _ t i m e ( x ) = ∑ i = 1 n ( n − i ) T i f_{min\_time}(x)= \sum_{i=1}^{n} (n-i)T_i fmin_time(x)=i=1n(ni)Ti
此题通过简单的排序算法,即可解决。代码如下:

#include <iostream>
#include <iomanip>
using namespace std;
int main() {double t = 0;int n;int a[1001][2];cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i][0];a[i][1] = i;}for (int i = 1; i <= n ; i++) {for (int j = 1; j <= n - i; j++) {if (a[j][0] > a[j + 1][0]) {int temp = a[j + 1][0];a[j+1][0] = a[j][0];a[j][0] = temp;int temp1 = a[j + 1][1];a[j+1][1] = a[j][1];a[j][1] = temp1;}}}for (int i = 1; i <= n; i++) {cout << a[i][1] << ' ';t += (n - i) * a[i][0];}t = t / n;cout << fixed << setprecision(2) <<endl <<t;
}


三、总结

贪心算法所作的选择来源于以往的选择,并非将来的选择。贪心算法相对于其他算法有一定的速度优势,在一题可以多解的情况下,可以优先选择贪心算法。

http://www.lryc.cn/news/122149.html

相关文章:

  • 【Windows10下启动RocketMQ报错:找不到或无法加载主类 Files\Java\jdk1.8.0_301\lib\dt.jar】解决方法
  • 深入篇【Linux】学习必备:进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级)
  • Python 潮流周刊#15:如何分析 FastAPI 异步请求的性能?
  • 基于Java+SpringBoot+Vue的网吧管理系统设计与实现(源码+LW+部署文档等)
  • redis设置database 不生效剖析
  • 汽车及汽车零部件行业云MES解决方案
  • 算法工程师-机器学习面试题总结(4)
  • Linux学习之awk函数
  • Redis的数据结构到底是一种什么样的结构?
  • eclipse 导入项目js报错问题
  • 《HeadFirst设计模式(第二版)》第七章代码——外观模式
  • 前端杂项-个人总结八股文的背诵方案
  • 利用 3D 地理空间数据实现Cesium的沉浸式环境
  • 微服务——ES实现自动补全
  • 北斗+5G 织就精确定位的“天罗地网”
  • Ansible Roles详解
  • 微服务学习笔记-基本概念
  • Linux查看GPU显卡/CPU内存/硬盘信息
  • SQLAlchemy 入门:Python 中的 SQL 工具包和 ORM
  • react Hook+antd封装一个优雅的弹窗组件
  • HICP学习--BGP综合小实验
  • grafana中利用变量来添加dashboard详情页地址实现点击跳转
  • 正则表达式练习
  • leetcode做题笔记73矩阵置零
  • 【2.1】Java微服务: Nacos的使用
  • vue安装及环境配置
  • java使用正则表达式时遇到的问题
  • Git介绍及常用命令详解
  • 赛事 | 第25届中国机器人及人工智能大赛全国决赛榜单发布
  • JavaScript+Asp.Net MVC5同时下载多个文件