重构贪心算法(一)

简介

贪心是信息学竞赛中一种很重要的解题方法,他不仅仅是一些题目的正确解法,还可能是一道题目中的部分算法,甚至还可以以错误的算法"骗得"更多的分数。在信息学竞赛中,很多题目往往要求最优解,这就和我们贪心的思维不谋而合,虽然很多时候贪心并不是正解,或者贪心的效率比较低,但是在我们不会做这道题的时候却能带给我们正确的思路。虽然他只是一种基本的算法,但是他的作用不逊于很多高级算法。

那么贪心究竟是什么呢?并没有标准答案,在每个人心中,贪心的定义是不同的,思想不同,算法也不同。但是他们都有一个共同的目的------将思维简单化

贪心是一个人的天性,在日常生活中,也会用到贪心:

1、选择路径的时候选择最优路径。

2、购买商品购买自己最喜欢的商品。

3、吃饭时选择评分最高的商店。

接下来就让我们从一些问题来入手贪心算法,先了解一下简单的贪心算法

例题

装载问题

给定 n n n个物品,每一个物品都有一个重量 w i wi wi,现要求选择尽量多的物体,使得总重量不超过 m m m。

该题要求的是装更多的物品,而不是更重的物品,对于同样一件物品,肯定优先选择重量更小的,这样可以给后面留更多的空间,接着再从剩下的里面选择一个最小的,至少物品选完或者无法再选择物品为止。每次要快速找到剩余物品中最小的,最好的方式就是根据物品重量从小到大排序。

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

题目描述

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

4 50
10 60
20 100
30 120
15 45

样例输出 #1

240.00

很容易想到先拿面额最大的金币,即价值/重量最大的金币,所以我们先对金币的价值/重量从大到小排序,注意排序的时候尽量不要写成除法形式,防止丢精度,最好写成乘法的形式,即: y 1 / x 1 > y 2 / x 2 y1/x1>y2/x2 y1/x1>y2/x2最好写成: y 1 ∗ x 2 > y 2 ∗ x 1 y1*x2>y2*x1 y1∗x2>y2∗x1;接着按照顺序依次取走金币,如果背包剩余空间大于等于金币重量,则取走这一堆金币,如果背包剩余空间小于金币重量,则取走背包剩余空间的金币重量,按照比例算出金币的价值。

[NOIP2002 提高组] 均分纸牌

题目描述

有 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 N−1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

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

例如 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,表示每堆纸牌初始时的纸牌数。

输出格式

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

样例 #1

样例输入 #1

4
9 8 17 6

样例输出 #1

3

提示

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

【题目来源】

NOIP 2002 提高组第一题

因为要保证最后的纸牌数量都是相同的,所以在总的纸牌数量一定的情况下,最终每堆纸牌的数量是确定的,我们只需要考虑把每堆纸牌变成最终的结果即可。

考虑的时候,最好从开头或者末尾考虑,因为只有这两堆纸牌只能往左边或者右边移动,其他纸牌都可以左右两边移动。先考虑第一堆纸牌,由于只能由第二堆纸牌移动过来,所以移动的次数可以通过减法得到,而同样的,第二堆纸牌的数量也会相应变化,接下来考虑第二堆,由于第一堆纸牌数量已经争取了,所以只能和第三堆纸牌相互移动。最后累计答案。

田忌赛马

题目描述

我国历史上有个著名的故事: 那是在 2300 2300 2300 年以前。齐国的大将军田忌喜欢赛马。他经常和齐王赛马。他和齐王都有三匹马:常规马,上级马,超级马。一共赛三局,每局的胜者可以从负者这里取得 200 200 200 银币。每匹马只能用一次。齐王的马好,同等级的马,齐王的总是比田忌的要好一点。于是每次和齐王赛马,田忌总会输 600 600 600 银币。

田忌很沮丧,直到他遇到了著名的军师――孙膑。田忌采用了孙膑的计策之后,三场比赛下来,轻松而优雅地赢了齐王 200 200 200 银币。这实在是个很简单的计策。由于齐王总是先出最好的马,再出次好的,所以田忌用常规马对齐王的超级马,用自己的超级马对齐王的上级马,用自己的上级马对齐王的常规马,以两胜一负的战绩赢得 200 200 200 银币。实在很简单。

如果不止三匹马怎么办?这个问题很显然可以转化成一个二分图最佳匹配的问题。把田忌的马放左边,把齐王的马放右边。田忌的马 A 和齐王的 B 之间,如果田忌的马胜,则连一条权为 200 200 200 的边;如果平局,则连一条权为 0 0 0 的边;如果输,则连一条权为 − 200 -200 −200 的边......如果你不会求最佳匹配,用最小费用最大流也可以啊。 然而,赛马问题是一种特殊的二分图最佳匹配的问题,上面的算法过于先进了,简直是杀鸡用牛刀。现在,就请你设计一个简单的算法解决这个问题。

输入格式

第一行一个整数 n n n ,表示他们各有几匹马(两人拥有的马的数目相同)。第二行 n n n 个整数,每个整数都代表田忌的某匹马的速度值($0 \le $ 速度值 ≤ 100 \le 100 ≤100)。第三行 n n n 个整数,描述齐王的马的速度值。两马相遇,根据速度值的大小就可以知道哪匹马会胜出。如果速度值相同,则和局,谁也不拿钱。

输出格式

仅一行,一个整数,表示田忌最大能得到多少银币。

样例 #1

样例输入 #1

3
92 83 71
95 87 74

样例输出 #1

200

提示

数据规模与约定

  • 对于 20 % 20\% 20% 的数据, 1 ≤ N ≤ 65 1\le N\le 65 1≤N≤65;
  • 对于 40 % 40\% 40% 的数据, 1 ≤ N ≤ 250 1\le N\le 250 1≤N≤250;
  • 对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2000 1\le N\le2000 1≤N≤2000。

这道题其实是一道非常经典的动态规划的题,但是嘛,可以用贪心来写

学过田忌赛马这篇课文的同学都知道田忌是如何赢的,但是实际操作中还需要多考虑一些问题:

1、如果田忌的最快马比齐王的最快马快,那么肯定是直接拿这两匹马比较,田忌赢。

2、如果田忌的最快马比齐王的最快马慢,那么就用田忌的最慢马与齐王的最快马比较,田忌输。

3、如果最快马相等,那么就该比较最慢马了,如果田忌的最慢马比齐王的最慢马快,肯定直接拿这两匹马比较,田忌赢。

4、如果最快马相等且田忌的最慢马比齐王的最慢马慢,那么就用田忌的最慢马与齐王的最快马比较,田忌输。

5、如果田忌的最快马和最慢马都和齐王的马速度相同,注意还是用田忌的最慢马和齐王的最快马比较,如果最快马和最快马比较,最慢马和最慢马比较,答案不是最优的,比如321和321。

6、看上去把所有情况都考虑完了,而且没有相同速度的马比较的情况,但是可能剩下的马速度都是一样的,所以对于剩下的所有情况,就是不输不赢,并且消耗掉这两匹马。

当所有的马都比较结束后,答案就出来了,为了保证马的顺序性,我们需要先对马的速度排序。

我没有放代码,因为我们这里是先感受一下贪心算法,至于贪心算法的一些技巧我们下一回来讲

相关推荐
Lenyiin20 分钟前
02.06、回文链表
数据结构·leetcode·链表
爪哇学长23 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
爱摸鱼的孔乙己25 分钟前
【数据结构】链表(leetcode)
c语言·数据结构·c++·链表·csdn
Dola_Pan27 分钟前
C语言:数组转换指针的时机
c语言·开发语言·算法
繁依Fanyi40 分钟前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
烦躁的大鼻嘎1 小时前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
C++忠实粉丝1 小时前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
用户37791362947552 小时前
【循环神经网络】只会Python,也能让AI写出周杰伦风格的歌词
人工智能·算法
福大大架构师每日一题2 小时前
文心一言 VS 讯飞星火 VS chatgpt (396)-- 算法导论25.2 1题
算法·文心一言
EterNity_TiMe_2 小时前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip