pascal贪心算法.docx
- 文档编号:10628996
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:24
- 大小:52.95KB
pascal贪心算法.docx
《pascal贪心算法.docx》由会员分享,可在线阅读,更多相关《pascal贪心算法.docx(24页珍藏版)》请在冰豆网上搜索。
pascal贪心算法
贪心策略
当一个问题具有最优子结构时,一般我们会想到用动态规划法去解它,但是有些问题存在着更简单有效的方法,我们只要总是做出当前看来最好的选择就可以了。
贪心算法所作的选择可以依赖于以往所作过的选择,但决不依赖于将来的选择,也不依赖于子问题的解,这使得算法在编码和执行的过程中都有着一定的速度优势。
如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。
但是贪心算法并不是对所有的问题都能得到整体最优解或最理想的近似解,与搜索法等通法比较,它的适用区域相对狭窄许多,因此正确的分析、归纳、判断贪心法的应用是十分重要的。
同时我们也应该知道贪心是一种方法,而不是算法,贪心法的应用需要结合其他算法。
通常而言,贪心策略需要结合排序算法。
【例题】删数问题
【问题描述】
键盘输入一个高精度的正整数N,去掉其中任意S个数字后剩下的数字按原左右次序将组成一个新的正整数。
编程对给定的N和S,寻找一种方案使得剩下的数字组成的新数最小。
输入数据均不需要判错。
输出应包括所去掉的数字的位置和组成的新的正整数。
(N不超过240位)
【问题分析】
对于这道试题,我们可以采用这样一种解题方式:
因为要删除S个数字,我们可以一个一个的删,每一次删除的目的都是使剩下的数尽量小。
那么在每一次删除时,我们应该选择那个数字?
最大的数字还是最左的数字?
例如5768,通过观察可以知道删除7这个数字可以使剩余的数最小。
通过思考可以得出结论,每一次删除的数字应该是这个数字序列中降序子序列最左边的数字。
具体可以这样操作,按高位到低位的方向搜索递减区间。
若不存在递减区间,则删尾数符;否则删递减区间的首数符,这样形成了一个新的数串。
然后回到串首,重复上述规则,删下一数符…以此类推,直至删除S个数符为止。
例如:
N=“8796542”,S=4删数过程如下:
N=“8796542”{最左边的递减序列是87,删除“8”}
“796542”{最左边的递减序列是96542,删除9}
“76542”{最左边的递减序列是76542,删除“7”}
“6542”{最左边的递减序列是6542,删除“6”}
“542”
【程序代码】
programdelete;
vari,j,k,s:
integer;
N:
string;
begin
readln(n);
readln(s);
whiles>0dobegin
I:
=1;
while(i Inc(i); delete(n,I,1); dec(s); end; while(length(n)>1and(n[1]=‘0’)do delete(n,1,1); writeln(n); end. 【问题思考】 由例题可知,贪心算法的实现需要思考两个基本的问题: 如何建立和方法的正确性。 如何建立。 怎样从一个规模较小的解推出规模较大的解呢? 拿这个例题来说,从数串5768删除2位数的模拟过程中推广到240位的数串删数过程。 方法的正确性。 确保贪心算法确实是有效是使用贪心算法的关键所在。 确定贪心算法是有效的,那么解决该问题在时间复杂度还是在空间的使用方面与其他方法想比较显得更为优势。 以例题为例,删数方法是首先按高位到低位的方向搜索递减区间。 若不存在递减区间,则删尾数符;否则删递减区间的首数符,这样形成了一个新的数串。 这样获得的新数肯定是最小的新数。 对于上述例题,假如我们按照其他的删数过程是正确的,删除最左边的数或者每次删除最大的数。 同样以5768为例。 删除5得768,删除8得576,都不是最小的数值,假设不成立,证明这种方式是错误的,按照原先设定的方法所得到的解是最优解。 由上例可以获知,贪心的使用需要严格的证明,保证问题的严密性,贪心方法的使用还需呀结合其他算法的使用,例如: 排序、模拟、搜素、枚举等等,但是一旦证明了贪心方法使用的正确性,程序的运行效率会大大提高。 证明使用贪心方法的正确性依靠的是严密的数学头脑和以往的经验积累,假如不具备这些条件,使用该方法需要谨慎。 二.经典例题分析 1.混合牛奶(milk.pasUSACO练习题) 【问题描述】 牛奶包装是一个如此低利润的生意,以至于尽可能低地控制初级产品(牛奶)的价格变得十分重要。 请帮助Merry的牛奶制造公司(MerryMilkMakers')以尽可能最廉价的方式取得他们所需的牛奶。 Merry的牛奶制造公司从一些农民那购买牛奶,每个农民卖给牛奶制造公司的价格不一定相同。 而且,如一只母牛一天只能生产一定量的牛奶,农民每一天只有一定量的牛奶可以卖。 每天,Merry的牛奶制造公司从每个农民那购买一定量的牛奶,少于或等于农民所能提供的最大值。 现给出Merry牛奶制造公司的每日的牛奶需求,连同每个农民的可提供的牛奶量和每加仑的价格,请计算Merry的牛奶制造公司所要付出钱的最小值。 注意: 每天农民生产的牛奶的总数对Merry的牛奶制造公司来说是足够的。 【输入格式】 第1行: 二个整数,N和M。 第一个数值N(0<=N<=2,000,000)是Marry的牛奶制造公司的一天需要牛奶的数量。 第二个数值,M,(0<=M<=5,000)是提供牛奶的农民个数。 第2到M+1行: 每行二个整数,Pi和Ai。 Pi(0<=Pi<=1,000)是农民i的牛奶的价格;Ai(0<=Ai<=2,000,000)是农民i一天能卖给Marry的牛奶制造公司的牛奶数量。 【输出格式】 单独的一行包含单独的一个整数,表示Marry的牛奶制造公司拿到所需的牛奶所要的最小费用。 【样例输入】【样例输出】 1005630 520 940 310 880 630 2.门口的树(trees.pas) 【问题描述】 现在我们国家开展新农村建设,农村的住房建设纳入了统一规划,统一建设,政府要求每一住户门口种些树。 门口路边的地区被分割成块,并被编号成1..N。 每个部分为一个单位尺寸大小并最多可种一棵树。 每个居民房子门前被指定了三个号码B,E,T。 这三个数表示该居民想在B和E之间最少种T棵树。 当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l。 居民们想种树的各自区域可以交叉。 你的任务是求出能满足所有要求的最少的树的数量,尽量较少政府的支出。 【输入格式】 第一行包含数据N,M,区域的个数(0 下面的m行描述居民们的需要: BET,0 【输出格式】 输出文件第一行写有树的数目,下面的行包括所有树的位置,相邻两数之间用一个空格隔开。 【样例输入】【样例输出】 945 35234689 142 462 892 3.最大连接数(NOIP1998年提高组决赛第2题maxnumber.pas) 【问题描述】 设有n个正整数,将他们连接成一排,组成一个最大的多位整数。 例如: n=3时,3个整数13,312,343连成的最大整数为: 34331213。 又如: n=4时,4个整数7、13、4、246连接成的最大整数为7424613。 【输入文件】 两行: 第一行单独一个N;第二行N个数。 【输出文件】 一行,连接成的多位数。 【输入样例】 3 13312343 【输出样例】 34331213 4.最大乘积(maxmul.pas) 【问题描述】 一个正整数一般可以分为几个互不相同的自然数的和,如3=1+2,4=1+3,5=1+4=2+3,6=1+5=2+4,…。 现在你的任务是将指定的正整数n分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。 【输入格式】 只一个正整数n,(3≤n≤10000)。 【输出格式】 第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。 第二行是最大的乘积。 【样例输入】【样例输入】 10235 30 【问题分析】 解决方法: 贪心策略。 初看此题,很容易想到用回溯法进行搜索,但是这里的n范围比较大,最多到10000,如果盲目搜索,运行时间比较长,效率很低,对于部分数据可能得到结果,对于大部分数据会超时或栈溢出。 思考要使乘积最大,尽可能地将指定的n(n>4)拆分成从2开始的连续的自然数的和,如果最后有剩余的数,将这个剩余的数在优先考虑后面项的情况下平均分给前面的各项。 证明: 先来看看几个n比较小的例子,看能否从中找出规律: n 分解方案 最大的乘积 5 23 6 6 24 8 7 34 12 8 35 15 9 234 24 10 235 30 可以发现,将n分解成a1,a2,a3,a4,…,am这m个自然数,该序列为从2开始的一串由小到大的自然数,如果a1为1,则对乘积没有影响,而且使n减少,没有实际意义,只有特殊情况如n为3、4时才可能用上。 设h>=5,可以证明当将h拆分为两个不相同的部分并且两部分都大于1时两部分的乘积大于h。 证明如下: 将h分为两部分: a,h-a其中2<=a a*(h-a)-h=h*a-a*a-h=h*(a-1)-a*a 因为h>2*a,所以a*(h-a)-h>2*a*(a-1)-a*a=a*a-2*a=a*(a-2) 又因为a>=2,所以a*(a-2)>=0,所以a*(h-a)-h>O即a*(h-a)>h。 从上面的证明可以看出,对于指定的正整数,如果其大于等于5,将它拆分为不同的部分后乘积变大,对于中间结果也是如此。 因此可以将指定的n,依次拆成a1+a2+a3+a4+…+am,乘积最大。 现在的问题是如何拆分才能保证n=a1+a2+a3+a4+…+am呢? 可以先这样取: 当和不足n时,a1取2,a2取3,…,am-1取m,即从2开始按照自然数的顺序取数,最后剩余的数给am,如果am<=am-1,此时am跟前面的数字出现了重复,则把am从后面开始平均分布给前面的m-1个数。 为什么要从后面开始往前呢? 同样是考虑数据不出现重复的问题,如果是从前面往后面来平均分配,如2加上1以后变成3,就跟后面的已有的3出现了重复。 这样操作到底是否正确、是否能保证乘积最大呢? 还要加以证明。 证明过程如下: 设两个整数a,b的和为2s,且a<>b,设a=s-1,则b=s+1,a*b=(s-1)*(s+1)=s2-1,如果a=s-2,则b=s+2,a*b=(s-2)*(s+2)=s2-4。 a-b的绝对值越小,乘积的常数项越大,即乘积越大,上面的序列a1,a2,a3,a4,…,am正好满足了a-b的绝对值最小。 但是还要注意两个特例就是n=3和n=4的情况,它们的分解方案分别为1,2和1,3,乘积分别为2和3。 以n=10为例,先拆分为: 10=2+3+4+1,最后一项为1,比4小,将其分配给前面的一项,得到10=2+3+5,所以最大的乘积为2*3*5=30。 以n=20为例,拆分为: 20=2+3+4+5+6,正好是连续自然数的和,所以最大乘积为2*3*4*5*6=720。 再以n=26为例,先拆分为: 26=2+3+4+5+6+6,因为最后一项为6,不比最后第二项大,所以将其平均分给前面的项,优先考虑后面的项,即前面的4项各分到1,笫5项6分到2,最后是26=3+4+5+6+8,所以最大的乘积为3*4*5*6*8=2880。 由于n可能大到10000,分解之后的各项乘积位数比较多,超过普通的数据类型的位数,所以要用到高精度运算来进行整数的乘法运算,将结果保存在数组里。 5.合并果子(fruit.pasNOIP2004年提高组第2题) 【问题描述】 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。 多多决定把所有的果子合成一堆。 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。 可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。 多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。 假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。 例如有3种果子,数目依次为1,2,9。 可以先将1、2堆合并,新堆数目为3,耗费体力为3。 接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。 所以多多总共耗费体力=3+12=15。 可以证明15为最小的体力耗费值。 【输入格式】 输入文件fruit.in包括两行。 第一行是一个整数n(1≤n≤10000),表示果子的种类数。 第二行包含n个整数,用空格分隔,第i个整数ai(1≤ai≤20000)是第i种果子的数目。 【输出格式】 输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。 输入数据保证这个值小于231。 【输入样例】【输出样例】 315 129 【数据规模】 对于30%的数据,保证有n<=1000; 对于50%的数据,保证有n<=5000; 对于全部的数据,保证有n<=10000。 【问题分析】 解决方法: 模拟+排序+贪心。 根据样例数据可知,果子数越小的堆,应越早合并,样例模拟合并的过程就是算法实现的过程。 首先对所有的果子有小到大进行排序,合并最小果子堆,重新排序,继续合并最小果子堆,重复至完成所有果子的合并,得出最终的体力消耗值。 程序代码采用的就是这种方式。 但是不难发现,上面的方法需要进行n-1次的排序,比较浪费时间,改变一下思维方式,用空间换取时间的方式,另设一个数组b存放每次合并后的堆。 具体方式,首先读入n堆果子的重量,并按递增顺序进行排序,得到序列a[1]…a[n],并增加两个数a[n+1]=a[n+2]=maxinteger。 设b序列初始值为空,然后从a序列和b序列的第一个元素开始寻找合并方案,并设定x和y分别为数组a和数组b的移动指针变量。 这样,每次合并的方案不在乎以下三种方案: 1合并两个初始堆(a[x]和a[x+1]),合并后指针变量移动为: x: =x+2; 2一个初始堆a与另一个合并堆b进行合并(a[x]+b[y]),合并后指针变量移动为: x: =x+1;y: =y+1; 3两个合并堆b进行合并(b[y]+b[y+1]),合并后的指针移动为: y: =y+2; 不难得出,每次合并后最小体力消耗值为: Total: =total+min{a[x]+a[x+1],a[x]+b[y],b[x]+b[x+1]}。 6.守望者的逃离(escape.pas2007年普及组第3题) 【问题描述】 恶魔猎手尤迫安野心勃勃,他背叛了暗夜精灵,率深藏在海底的那加企图叛变,守望者在与尤迪安的交锋中遭遇了围杀。 被困在一个荒芜的大岛上。 为了杀死守望者,尤迪安开始对这个荒岛施咒,这座岛很快就会沉下去,到那时岛上的所有人都会遇难: 守望者的跑步速度为17m/s,以这样的速度是无法逃离荒岛的。 庆幸的是守望者拥有闪烁法术,可在1s内移动60m,不过每次使用闪烁法术都会消耗魔法值10点。 守望者的魔法值恢复的速度为4点/s,只有处在原地休息状态时才能恢复。 现在已知守望者的魔法初值M,他所在的初始位置与岛的出口之间的距离S,岛沉没的时间T。 你的任务是写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望者在剩下的时间内能走的最远距离。 注意: 守望者跑步、闪烁或休息活动均以秒(s)为单位。 且每次活动的持续时间为整数秒。 距离的单位为米(m)。 【输入文件】 输入文件escape.in仅一行,包括空格隔开的三个非负整数M,S,T。 【输出文件】 输出文件escape.out包含两行: 第1行为字符串“Yes”或“No”(区分大小写),即守望者是否能逃离荒岛。 第2行包含一个整数,第一行为“Yes”(区分大小写)时表示守望着逃离荒岛的最短时间第一行为“No”(区分大小写)时表示守望者能走的最远距离。 【样例1输入】【样例1输出】 392004No 197 【样例2输入】【样例2输出】 3625510Yes 6 7.排座椅(seat.pasNOIP2008年普及组第2题) 【问题描述】 上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任十分头疼的一件事情。 不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下来之后,只有有限的D对同学上课时会交头接耳。 同学们在教室中坐成了M行N列,坐在第i行第j列的同学的位置是(i,j),为了方便同学们进出,在教室中设置了K条横向的通道,L条纵向的通道。 于是,聪明的小雪想到了一个办法,或许可以减少上课时学生交头接耳的问题: 她打算重新摆放桌椅,改变同学们桌椅间通道的位置,因为如果一条通道隔开了两个会交头接耳的同学,那么他们就不会交头接耳了。 请你帮忙给小雪编写一个程序,给出最好的通道划分方案。 在该方案下,上课时交头接耳的学生对数最少。 【输入格式】 输入文件的第一行,有5个用空格隔开的整数,分别是M,N,K,L,D(2<=N,M<=1000,0<=K 接下来D行,每行有4个用空格隔开的整数,第i行的4个整数Xi,Yi,Pi,Qi,表示坐在位置(Xi,Yi)与(Pi,Qi)的两个同学会交头接耳(输入保证他们前后相邻或者左右相邻)。 输入数据保证最优方案的唯一性。 【输出格式】 输出文件共两行。 第一行包含K个整数,a1a2……aK,表示第a1行和a1+1行之间、第a2行和第a2+1行之间、…、第aK行和第aK+1行之间要开辟通道,其中ai 第二行包含L个整数,b1b2……bk,表示第b1列和b1+1列之间、第b2列和第b2+1列之间、…、第bL列和第bL+1列之间要开辟通道,其中bi 【输入样例】【输出样例】 451232 424324 2333 2524 【输入输出样例解释】 上图中用符号*、※、+标出了3对会交头接耳的学生的位置,图中3条粗线的位置表示通道,图示的通道划分方案是唯一的最佳方案。 8.牛棚问题(barn.pasUSACO练习题) 【问题描述】 在一个暴风雨的夜晚,农民约翰的牛棚的屋顶、门被吹飞了。 好在许多牛正在度假,所以牛棚没有住满。 剩下的牛一个紧挨着另一个被排成一行来过夜。 有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。 自门遗失以后,农民约翰必须尽快在牛棚之前竖立起新的木板。 他的新木材供应者将会供应他任何他想要的长度,但是供应者只能提供有限数目的木板,农民约翰想将他购买的木板总长度减到最少。 M(1<=M<=50)可能买到的木板最大的数目; S(1<=S<=200),牛棚的总数; C(1<=C<=S)牛棚里牛的数目,和牛所在的牛棚的编号stall_number(1<=stall_number<=S),计算拦住所有有牛的牛棚所需木板的最小总长度。 输出所需木板的最小总长度作为的答案。 【输入格式】 第1行: M,S和C(用空格分开) 第2到C+1行: 每行包含一个整数,表示牛所占的牛棚的编号。 【输出格式】 单独的一行包含一个整数表示所需木板的最小总长度。 【样例输入】 45018 3 4 6 8 14 15 16 17 21 25 26 27 30 31 40 41 42 43 【样例输出】 25(一种最优的安排是用板拦住牛棚3-8,14-21,25-31,40-43。 ) 练习题目: 1.花生采摘(peanuts.pas2004年普及组第2题) 【问题描述】 鲁滨逊先生有一只宠物猴,名叫多多。 这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条: ‘欢迎免费品尝我种的花生! ————熊字。 ‘鲁滨逊先生和多多都很开心,因为花生正是他们的最爱。 在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成巨型网络-图1-有经验的多多一眼就能看出,每棵花生植株下的花生有多少。 为了训练多多的算术,鲁滨逊先生说: ’你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依次类推,不过你一定要在我限定的时间内回到路边。 ‘我们假定多多在每个单位时间内,可以做下列4件事情中的一件: (1)从路边跳到最靠近路边(即第1行)的某棵花生植株; (2)从一棵植株跳到前后左右与之相邻的另一棵植株; (3)采摘一棵植株下的花生; (4)从最靠近路边(即第1行)的某棵花生植株跳回路边。 现在给定一块花生田的大小和花生的分布,请问在限定时间内,多多最多可以采到多少个花生? 注意可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。 【输入格式】 输入文件第一行3个整数,M,N,K空格隔开;表示花生田的大小为M*N(1〈=M,N〈=20〉,多多采花生的限定时间为K(0<=K<=1000)个单位时间。 接下来的M行,每行包括N个非负整数,也用空格隔开;第i+1行的第j个整数Pij(0<=Pij<=500)表示花生田里植株(i,j)下花生的数目,0表示该植株下没有花生。 【输出格式】 输出文件,只有一行包含一个整数,即在限定时间内,多多最多可以采到的花生个数。 【输入样例1】 6721 0000000 00001300 0000007 01500000 0009000 0000000 【输出样例1】 6720 0000000 00001300 0000007 01500000 0009000 0000000 【输出样例2】 28 2.导弹拦截(missile.pasNOIP1999提高组第1题) 【问题描述】 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷: 虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。 某天,雷达捕捉到敌国的导弹来袭。 由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。 输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。 【输入文件】 单独一行列出导弹依次飞来的高度。 【输出文件】 两行,分别是最多能拦截的导弹数,要拦截所有导弹最少要配备的系统数 【输入样例】 38920715530029917015865 【输出样例】 6 2 3.均分纸牌(solitaire.pasNOIP2002年提高组第1题) 【问题描述】 有N堆纸牌,编号分别为1,2,…,N。 每堆上有若干张,但纸牌总数必为N的倍数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- pascal 贪心 算法