NOIP--基本算法设计--贪心

1. 概述:    

贪心算法(又称贪婪算法)是指,在对问题求解时,不从整体最优上加以考虑,而是做出一个看上去最优的决策(即局部最优解),并期望通过每次所做的局部最优解产生全局最优解。

2.知识点梳理:

Ø  贪心算法设计

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。具有该性质的问题才可以用贪心算法求解。

贪心算法的设计,主要是找到合适的局部最优值,使得一直取该最优值后,原问题到达最优解。找合适的局部最优值过程,就是一个设计估价函数或比较函数的过程,至于如何设计函数,不同的题目有不同的设计方式。在这里列出一些常见的贪心算法设计:

a.         最多物品背包问题:有n件物品和一个容量为C的背包。第i件物品的重量是w[i]。求解将哪些物品装入背包可物品数量最多。贪心策略:将物品重量从小到大进行排序,优先挑重量轻的装入背包。

b.        部分背包问题:有n件物品和一个容量为C的背包。第i件物品的重量是w[i],价值是v[i]。每个物体可以取走一部分,价值和重量按比例计算。求解将哪些物品装入背包可使价值总和最大。贪心策略:将背包按照单价(价值/重量的比值)排序。从物美价廉(单价尽可能大)的物体开始挑起,直到背包装满或没有物体可拿。

c.         乘船问题:有n个人,第i个人的重量是wi。每艘船的最大载重量都是C,且最多能乘两个人。用最少的船装尽可能多的人。贪心策略:让最重的人和能与他同船的最重的人乘一条船,如果办不到,那他就一人乘一条船。

d.        不相交区间问题:数轴上有n个开区间(ai, bi)。选择尽量多的区间,使这些区间两两没有公共点。贪心策略:按bi从小到大的顺序排序,然后选择第一个区间,接下来把所有与第一个区间相交的区间排除在外,继续原贪心操作。

e.         删数问题:给出一个N 位的十进制高精度数,要求从中删掉S个数字(其余数字相对位置不得改变),使剩余数字组成的数最小。贪心策略:每次找到最靠前的一对相邻逆序数(两数相邻,前数大于后数),删去前数,若找不到则删去最后一位数。另外,图论中的一些算法,例如最小生成树、最短路径等,都用了贪心的策略,此部分会在后续图论章节中详细讲述。

Ø  贪心算法证明

贪心算法在设计的过程中,其全局最优性需要证明。因为在比赛的过程中,往往会想到一些错误的贪心算法,如果缺乏证明,会导致整个程序出错。

贪心算法证明有很多方法,其中最重要的是反证法。即,假设如果最优解中的策略与贪心的策略不同,证明贪心的策略优于(或不差于)所假设的最优策略,从而得到假设不成立,贪心算法正确性证毕。

举个例子,对于最多物品背包问题的证明,假设有最优策略与贪心策略不同,即存在i,j,其中且物品i装入箱子,但物品j不装入箱子。那么,将i移除箱子,j装入箱子,其总重量小于原策略,假设不成立。因此,最优策略中,不存在刚才假设的情况,即,满足贪心策略。

Ø  贪心算法的不足

贪心算法不仅在代码编写上简单,其时间复杂度也很低,但是不是所有问题都能用贪心算法设计的。

举个例子:背包问题:有n件物品和一个容量为C的背包。第i件物品的重量是w[i]。求解将哪些物品装入背包后,使得剩余容量最小。

本题很容易想到的贪心策略是一次可放入背包的重量最大的物品放入。但是该贪心策略是错误的。例如背包容量为10,物品重量为5,4,4,2。放入5,4的策略差与放入4,4,2。本题正确的解法为动态规划,在后续动态规划章节会详细讲述。

3.重难点分析:

u 贪心策略应选取妥当。

u 贪心算法需要证明,即便是不完备的证明。

u 有些问题贪心算法不能求解。
4.例题解析:

例题4-1:合并果子(NOIP2004)

【问题描述】在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

【输入文件】输入包括两行,第一行是一个整数n (1≤n≤10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai (1≤ai≤20000)是第i种果子的数目。

【输出文件】输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。

【样例输入】

3

129

【样例输出】15

【分析】本题为“哈夫曼编码”为题,求解方式为贪心。贪心策略为,每次选取两堆重量最小的果子,合并成一堆,并记录耗费的体力值。当果子只剩下一堆时,算法结束。利用“堆”来维护最小值,每次更新时间复杂度为O(log n),整个算法时间复杂度为O(n log n)。

例题4-2:国王的游戏(NOIP2012)

【问题描述】恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这n位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

【输入】输入第一行包含一个整数n,表示大臣的人数。

第二行包含两个整数a和b,之间用一个空格隔开,分别表示国王左手和右手上的整数。接下来n行,每行包含两个整数a和b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

【输出】输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

【样例输入】3

1  1

2  3

7  4

4  6

【样例输出】2

【样例说明】按1、2、3号大臣这样排列队伍,获得奖赏最多的大臣所获得金币数为2;

按1、3、2这样排列队伍,获得奖赏最多的大臣所获得金币数为2;

按2、1、3这样排列队伍,获得奖赏最多的大臣所获得金币数为2;

按2、3、1这样排列队伍,获得奖赏最多的大臣所获得金币数为9;

按3、1、2这样排列队伍,获得奖赏最多的大臣所获得金币数为2;

按3、2、1这样排列队伍,获得奖赏最多的大臣所获得金币数为9。

因此,奖赏最多的大臣最少获得2个金币,答案输出2。

【分析】首先理解题意,设排序后,n为大臣的顺序为1~n,国王的序号为0。令序号为i的人左右的数字为ai,右手数字为bi。,则序号为k和k+1的大臣获得的奖赏为:

 

 例题4-3:均分纸牌(NOIP2002)

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

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

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

例如 N=4,4 堆纸牌数分别为:①9,②8,③17,④6移动3次可达到目的:从③取4张牌放到④ (9,8,13,10);从③取3张牌放到② (9,11,10,10);从②取1张牌放到① (10,10,10,10)。

【输入】第一行一个数N,表示纸牌堆数 (1≤N≤100)第二行共N个数,A1 A2… An (N 堆纸牌,每堆纸牌初始数,l≤Ai≤10000)

【输出】所有堆均达到相等时的最少移动次数。

【样例输入】

4

9 8 17 6

【样例输出】3

【分析】本题算法为贪心算法,贪心策略为:先让起始状态的每一堆纸牌减去平均数(目标状态),然后从一段往另一端扫描,如果当前纸牌的数值不为零,那么让当前纸牌的数值累加到它右边那张牌,再把当前牌的数值置为0,总步数加一;如果当前纸牌数值为零则跳过执行下一张牌。最后统计出的步数即为所求结果。

 

NOIP信息学视频地址

视频地址

链接:https://pan.baidu.com/s/1tHo1DFMaDuMZAemNH60dmw 
提取码:7jgr

posted @ 2020-10-22 10:15  tianli3151  阅读(327)  评论(0编辑  收藏  举报