第三次课贪心算法_第1页
第三次课贪心算法_第2页
第三次课贪心算法_第3页
第三次课贪心算法_第4页
第三次课贪心算法_第5页
已阅读5页,还剩68页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第三次课贪心算法第一页,共七十三页,编辑于2023年,星期四第3章内容提纲3.1贪心算法的基本思想3.2删数问题3.3装箱问题第二页,共七十三页,编辑于2023年,星期四 顾名思义,贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。 当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路经问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。第三页,共七十三页,编辑于2023年,星期四活动安排问题

活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合,是可以用贪心算法有效求解的很好例子。该问题要求高效地安排一系列争用某一公共资源的活动。贪心算法提供了一个简单、漂亮的方法使得尽可能多的活动能兼容地使用公共资源。第四页,共七十三页,编辑于2023年,星期四活动安排问题设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si<fi。如果选择了活动i,则它在半开时间区间[si,fi)内占用资源。若区间[si,fi)与区间[sj,fj)不相交,则称活动i与活动j是相容的。也就是说,当si≥fj或sj≥fi时,活动i与活动j相容。第五页,共七十三页,编辑于2023年,星期四活动安排问题template<classType>voidGreedySelector(intn,Types[],Typef[],boolA[]){A[1]=true;intj=1;for(inti=2;i<=n;i++){if(s[i]>=f[j]){A[i]=true;j=i;}elseA[i]=false;}}下面给出解活动安排问题的贪心算法GreedySelector:各活动的起始时间和结束时间存储于数组s和f中且按结束时间的非减序排列

第六页,共七十三页,编辑于2023年,星期四活动安排问题 由于输入的活动以其完成时间的非减序排列,所以算法greedySelector每次总是选择具有最早完成时间的相容活动加入集合A中。直观上,按这种方法选择相容活动为未安排活动留下尽可能多的时间。也就是说,该算法的贪心选择的意义是使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。

第七页,共七十三页,编辑于2023年,星期四算法greedySelector的效率极高。当输入的活动已按结束时间的非减序排列,算法只需O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。如果所给出的活动未按非减序排列,可以用O(nlogn)的时间重排。第八页,共七十三页,编辑于2023年,星期四活动安排问题

例:设待安排的11个活动的开始时间和结束时间按结束时间的非减序排列如下:i1234567891011S[i]130535688212f[i]4567891011121314第九页,共七十三页,编辑于2023年,星期四活动安排问题

算法greedySelector的计算过程如左图所示。图中每行相应于算法的一次迭代。阴影长条表示的活动是已选入集合A的活动,而空白长条表示的活动是当前正在检查相容性的活动。第十页,共七十三页,编辑于2023年,星期四活动安排问题若被检查的活动i的开始时间Si小于最近选择的活动j的结束时间fi,则不选择活动i,否则选择活动i加入集合A中。

贪心算法并不总能求得问题的整体最优解。但对于活动安排问题,贪心算法greedySelector却总能求得的整体最优解,即它最终所确定的相容活动集合A的规模最大。这个结论可以用数学归纳法证明。第十一页,共七十三页,编辑于2023年,星期四贪心算法的基本要素 本节着重讨论可以用贪心算法求解的问题的一般特征。对于一个具体的问题,怎么知道是否可用贪心算法解此问题,以及能否得到问题的最优解呢?这个问题很难给予肯定的回答。但是,从许多可以用贪心算法求解的问题中看到这类问题一般具有2个重要的性质:贪心选择性质和最优子结构性质。

第十二页,共七十三页,编辑于2023年,星期四贪心算法的基本要素1、贪心选择性质所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。第十三页,共七十三页,编辑于2023年,星期四贪心算法的基本要素

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。2、最优子结构性质第十四页,共七十三页,编辑于2023年,星期四贪心算法的基本要素 贪心算法和动态规划算法都要求问题具有最优子结构性质,这是2类算法的一个共同点。但是,对于具有最优子结构的问题应该选用贪心算法还是动态规划算法求解?是否能用动态规划算法求解的问题也能用贪心算法求解?下面研究2个经典的组合优化问题,并以此说明贪心算法与动态规划算法的主要差别。3、贪心算法与动态规划算法的差异第十五页,共七十三页,编辑于2023年,星期四贪心算法的基本要素0-1背包问题:

给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容量为C。应如何选择装入背包的物品,使得装入背包中物品的总价值最大?在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。第十六页,共七十三页,编辑于2023年,星期四贪心算法的基本要素背包问题:

与0-1背包问题类似,所不同的是在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包,1≤i≤n。这2类问题都具有最优子结构性质,极为相似,但背包问题可以用贪心算法求解,而0-1背包问题却不能用贪心算法求解。

第十七页,共七十三页,编辑于2023年,星期四贪心算法的基本要素

首先计算每种物品单位重量的价值Vi/Wi,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直地进行下去,直到背包装满为止。 具体算法可描述如下页:

用贪心算法解背包问题的基本步骤:第十八页,共七十三页,编辑于2023年,星期四贪心算法的基本要素voidKnapsack(intn,floatM,floatv[],floatw[],floatx[]){Sort(n,v,w);inti;for(i=1;i<=n;i++)x[i]=0;floatc=M;for(i=1;i<=n;i++){if(w[i]>c)break;x[i]=1;c-=w[i];}if(i<=n)x[i]=c/w[i];}

算法knapsack的主要计算时间在于将各种物品依其单位重量的价值从大到小排序。因此,算法的计算时间上界为O(nlogn)。为了证明算法的正确性,还必须证明背包问题具有贪心选择性质。第十九页,共七十三页,编辑于2023年,星期四贪心算法的基本要素 对于0-1背包问题,贪心选择之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每公斤背包空间的价值降低了。事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再作出最好选择。由此就导出许多互相重叠的子问题。这正是该问题可用动态规划算法求解的另一重要特征。 实际上也是如此,动态规划算法的确可以有效地解0-1背包问题。第二十页,共七十三页,编辑于2023年,星期四3.2删数问题 给定一个高精度的正整数N(不超过240位),去掉任意S个数字后剩下的数字按原左右次序组成一个新的正整数。编程对于给定的N和S,寻找一种方案使得剩下的数字组成的新数最小示例:

N=178543,S=4

则结果为:13第二十一页,共七十三页,编辑于2023年,星期四算法分析N的存储只能采用数组,可选择字符数组;可采用贪心算法,即:共删掉S个数,每一步删掉一个数,并且总选择一个使剩下的数最小的数符删去。为了保证删1个数符后的数最小,可以按照从高位到低位的方向收缩递减区间,如果不存在递减区间则删尾数符;否则删掉递减区间的首字符,这样形成一个新的数串。然后回到串首,重复上面的规则,删下一个数符,。。。,直至删除S个数符为止。第二十二页,共七十三页,编辑于2023年,星期四17854317543154314313第二十三页,共七十三页,编辑于2023年,星期四17853417534153413413第二十四页,共七十三页,编辑于2023年,星期四程序#include<iostream.h>#include<math.h>usingnamespacestd;ints;classnode{public: charc; node*next;};classquee{public: node*first;};第二十五页,共七十三页,编辑于2023年,星期四quee*init(){ charstr[250];quee*n;node*tmp;node*tail; n=newquee;n->first=NULL;cin>>str;cin>>s;inti=0; while(str[i]!='\0') { tmp=newnode; tmp->c=str[i];tmp->next=NULL; if(n->first==NULL) { n->first=tmp;tail=tmp; } else { tail->next=tmp;tail=tmp; } i++; } returnn;};第二十六页,共七十三页,编辑于2023年,星期四voiddel(quee*start,node*p){ node*x; x=start->first; if(x->next) { while(x->next!=p)x=x->next; } x->next=p->next; deletep;};第二十七页,共七十三页,编辑于2023年,星期四voidprint(quee*start){ node*tmp; if(start->first==NULL)return; tmp=start->first; while(tmp) { printf("%c",tmp->c); tmp=tmp->next; } printf("\n");};第二十八页,共七十三页,编辑于2023年,星期四node*find(quee*start){ node*tmp; tmp=start->first; while(tmp->next!=NULL&&tmp->c<=tmp->next->c) tmp=tmp->next; returntmp;};第二十九页,共七十三页,编辑于2023年,星期四intmain(intargc,char*argv[]){ quee*n; node*p; n=init(); while(s!=0) { p=find(n); del(n,p); s--; } print(n); return0;}第三十页,共七十三页,编辑于2023年,星期四3.3POJ1017Packets第三十一页,共七十三页,编辑于2023年,星期四Packets题意已知:有6*6的大箱子和1*1,2*2,3*3,4*4,5*5,6*6的木块(平面)问:给定各种木块的数目,求最少需要多少个大箱子来装?例如:输入:004001-〉输出2输入:751000-〉输出1第三十二页,共七十三页,编辑于2023年,星期四Packets解题思想:贪心准则:先放大的,后放小的6*6的木块每个占用一个箱子;5*5的木块每个占用一个新箱子,余下11个1*1的空格;4*4的木块每个占用一个新箱子,余下5个2*2的空格。4.3*3的木块每4个占用新一个箱子,不足4个也占一个新箱子,分情况余下不同数目的空格;5.2*2的木块先填空格,空格不足开新箱子,每9个2*2的木块占一个新箱子;6.1*1的木块先填空格,空格不足开新箱子,每36个占一个新箱子。第三十三页,共七十三页,编辑于2023年,星期四Packets假设6*6,5*5,4*4,3*3,2*2,1*1的箱子个数b6b5b4b3b2b1第三十四页,共七十三页,编辑于2023年,星期四Packets

4*4,5*5,6*6的块单独开新的箱子。每一个5*5的块还能放下11个1*1的箱子。每一个4*4的块还能放下5个2*2的箱子,或者放下20个1*1的箱子。4*4块5*5块6*6块第三十五页,共七十三页,编辑于2023年,星期四

3*3的块1-4块占一个新箱子,一个箱子可放下4个3*3的块。如果一个箱子放下了1个3*3的块,则还可放下5个2*2的块和7个1*1的块。或者可以放下27个1*1的块。如果一个箱子放下了2个3*3的块,则还可放下3个2*2的块和6个1*1的块。或者可以放下18个1*1的块。如果一个箱子放下了3个3*3的块,则还可放下1个2*2的块和5个1*1的块。或者可以放下9个1*1的块。第三十六页,共七十三页,编辑于2023年,星期四PacketsnTotal-箱子数1)先放好所有6*6,5*5,4*4和3*3的木块

nTotal=b6+b5+b4+(b3+3)/44*4,5*5,6*6单独开新的箱子

3*3每1-4个占一个新箱子2)再把2*2的塞到放有3*3木块的箱子里 intContain2[4]={0,5,3,1}; Contain2[i]表示当箱子里有i个3*3的木块时,还能放下多少个2*2的木块 3)考虑放有3*3的木块的箱子在塞满2*2的木后还能放多少个1*1的木块: intContain1[4]={0,7,6,5};

Contain1[i]表示当箱子里有i个3*3的木块,并且还填满了2*2的木块后,还能放下多少个1*1的木块。第三十七页,共七十三页,编辑于2023年,星期四Packets

如果箱子里放1个3*3木块,那么还能放5个2*2木块,以及7个1*1木块第三十八页,共七十三页,编辑于2023年,星期四Packets

如果箱子里放2个3*3木块,那么还能放3个2*2木块,以及6个1*1木块第三十九页,共七十三页,编辑于2023年,星期四Packets

如果箱子里放3个3*3木块,那么还能放1个2*2木块,以及5个1*1木块第四十页,共七十三页,编辑于2023年,星期四Packets 4)计算放好6*6,5*5,4*4,3*3后留下多少空格能放2*2

c2=b4*5+Contain2[b3%4];留下多少空格能放1*1 c1=b5*11+Contain1[b3%4](能放2*2的地方暂不考虑放1*1,因为如果c2〉b2则多余的b2-c2个位置可以放4*(b2-c2)个1*1的箱子)第四十一页,共七十三页,编辑于2023年,星期四Packets

5)放2*2,如果不够放,就开新箱子,放完2*2,计算还剩多少空格能放1*1。

if(b2<=c2) c1+=(c2-b2)*4; else{ nTotal+=(b2-c2)/9;//每个空箱子可以放9个2*2 intr2=(b2-c2)%9; if(r2){ nTotal++; c1+=36-r2*4; } }第四十二页,共七十三页,编辑于2023年,星期四

6)放1*1并输出结果

if(b1>c1){ nTotal+=(b1-c1)/36; if((b1-c1)%36) nTotal++; } cout<<nTotal<<endl;Packets第四十三页,共七十三页,编辑于2023年,星期四#include<iostream.h>intContain1[4]={0,7,6,5};intContain2[4]={0,5,3,1};intnTotal;voidmain(){intb1,b2,b3,b4,b5,b6;for(;;){cin>>b1>>b2>>b3>>b4>>b5>>b6;if(b1==0&&b2==0&&b3==0&&b4==0&&b5==0&&b6==0) break; nTotal=b6+b5+b4+b3/4; if(b3%4)nTotal++; intc1; //当前能放1*1木块的空格数目

c1=b5*11; //每个放5*5的箱子,还能放11个1*1的木块c1+=Contain1[b3%4]; //加上3*3箱子里能放的数目

intc2;//当前能放2*2木块的空格数目(先不考虑将1*1的木块放在 //2*2的空格里)

c2=b4*5; //每个放5*4的箱子,还能放5个2*2的木块

c2+=Contain2[b3%4]; //加上3*3箱子里能放的数目第四十四页,共七十三页,编辑于2023年,星期四

if(b2<=c2) c1+=(c2-b2)*4; else{ nTotal+=(b2-c2)/9;//每个空箱子可以放9个2*2 intr2=(b2-c2)%9; if(r2){ nTotal++; c1+=36-r2*4; } } if(b1>c1){ nTotal+=(b1-c1)/36; if((b1-c1)%36) nTotal++; } cout<<nTotal<<endl;}}第四十五页,共七十三页,编辑于2023年,星期四〖案例3〗水位为使房屋买主可以评估洪水保险的开销,一家真实的房地产公司提供了一种客户端程序,此程序现实了可能被购买得房屋所在地区以100平方米为单位的海拔高度。雨水、雪水和管道破裂流出的水将会汇聚到海拔最低,因为水会从高处流向低处。为简化问题,我们假设水沿着排水沟从高出流向低处,并且水不会被土地所吸收。第四十六页,共七十三页,编辑于2023年,星期四从天气信息的档案中,我们得知了一个区域的典型储水量。作为预期的购房者,我们希望知道低处积水后的水位,以及完全被淹没在水中的面积占此地区的百分比(也就是,10平方米的海拔高度严格低于水平面)。你被要求写一个程序来给出结果。第四十七页,共七十三页,编辑于2023年,星期四输入数据输入包括区域描述所构成的一个序列。每个由一对整数m和n开始,每个都小于30,给定的区域都是100平方米。紧随其后的是m行,每行n个整数,给出地区海平面,以主行序给出。高度的单位是米,分别用正数或者负数表明高于或低于海平面。每个区域最后一个值是一个整数,用以指出有多少立方米的水聚集到此区域。m和n为两个0代表输入结束。第四十八页,共七十三页,编辑于2023年,星期四输出描述对每个区域,显示区域编号(1,2,…),水平面(以米为单位表示高于或低于海平面)以及此区域被水淹没的面积,每个占一行。水位和被水淹没面积的百分率显示时保留2位小数每个区域输出后跟一个空行。第四十九页,共七十三页,编辑于2023年,星期四样例332537455112349483271000000Region1Waterlevelis46.67meters.66.67percentoftheregionisunderwater.第五十页,共七十三页,编辑于2023年,星期四解题思路由于题目中特别声明水无论如何都会流到当前水面最低的地方,使得问题一下子简化了。很容易想到以下贪心算法:(1)把每个格子的高度排序;(2)以低格子到高格子的顺序填水,把水均匀的铺在当前的水面上,并不断更新当前水面面积,和剩余水量;(3)若剩余水量为0,输出当前水面高度和水面覆盖率。第五十一页,共七十三页,编辑于2023年,星期四注意1.水面每达到一个格子的高度,水面的面积也要随之扩大;2.高度有负值,水面的初始高度是最低格子的高度而不是0;3.考虑好若干个格子高度相等的情况;4.每个格子的面积是100;5.没有水时,覆盖率为0;6.水若刚好达到一个格子的高度,此格子不算被水覆盖;7.水面达到最高格子后,要把剩下的水均匀的铺在整个区域上;8.注意浮点数的计算误差,因为这个原因大家在POJ上都没有通过此题:-<第五十二页,共七十三页,编辑于2023年,星期四#include<stdio.h>#include<stdlib.h>inth[1000000];//保存每个格子的高度值,因不知m和n的范围,故设的比较大intcmp(constvoid*a,constvoid*b){return*((int*)a)-*((int*)b);}第五十三页,共七十三页,编辑于2023年,星期四main(){intt,m,n,nn; doublex,hw;inti;t=0;第五十四页,共七十三页,编辑于2023年,星期四

while(1){//输入

scanf("%d%d",&n,&m);if(n==0&&m==0)break; t++;nn=m*n;for(i=0;i<nn;i++){scanf("%d",&h[i]);}scanf("%lf",&x);//排序

qsort(h,nn,sizeof(int),cmp);第五十五页,共七十三页,编辑于2023年,星期四

i=0;hw=h[0];if(x>0){for(i=1;i<nn;i++){//在高度相等的情况下,扩大水面面积

while(i<nn&&h[i]==h[i-1]){i++;}if(i==nn||x<=100*i*(h[i]-h[i-1]))break;//若剩

下的水不够使水面达到下一格子的高度,退出

x-=i*(h[i]-h[i-1])*100;}hw=h[i-1];}第五十六页,共七十三页,编辑于2023年,星期四if(x>0){//若剩下水,就把它铺在当前的范围上

while(i<nn&&h[i]==h[i-1]){i++;}hw+=x/i/100;}//输出结果

printf("Region%d\n",t);printf("Waterlevelis%.2fmeters.\n",hw);printf("%.2fpercentoftheregionisunderwater.\n\n",i*100/double(nn));}}第五十七页,共七十三页,编辑于2023年,星期四〖案例4〗埃及分数埃及同中国一样,也是世界上文明的古国之一。古埃及人处理分数与众不同,他们一般只用分子为1的分数,例如:用1/3+1/15来表示2/5,用1/4+1/7+1/28表示3/7,等等。设计一个程序,把一个真分数表示为埃及分数之和的形式。第五十八页,共七十三页,编辑于2023年,星期四Input第一行:N表示有N组测试数据,每组测试数据为一行包含a,b(0〈a〈b〈1000)。Output每组测试数据若干个数,自小到大排列,依次是单位分数的分母。SampleInput11945SampleOutput5618第五十九页,共七十三页,编辑于2023年,星期四所谓埃及分数,是指分子为1的形式。古代埃及有一个非常奇怪的习惯,他们喜欢把一个分数表示为若干个分子为1的分数之和的形式。如,7/8=1/2+1/3+1/24。下面介绍其中一种算法――贪心算法。贪心算法是由数学家菲波那契提出的,基本思想是:设某个真分数的分子为A,分母为B;把B除以A的商的整数部分加1后的值作为埃及分数的某一个分母;将A乘以C减去B作为新的A;将B乘以C作为新的B;如果A大于1且能整除B,则最后一个分母为B/A;如果A=1,则最后一个分母为B;否则转步骤(2)。如:7/8=1/2+1/3+1/24第六十页,共七十三页,编辑于2023年,星期四解题步骤:用变量A表示分子,变量B表示分母;C=B\A+1A=A*C-B,B=B*C打印1/C若A>1且B/A=B\A,则C=B/A若A=1,则C=B,打印1/C转步骤(2)。第六十一页,共七十三页,编辑于2023年,星期四#include<stdio.h>#include<stdlib.h>intnum;inta,b;intc;intzc(inta,intb){intxx;xx=b/a;if(xx*a==b)return1;return0;};第六十二页,共七十三页,编辑于2023年,星期四voidcalab(){while(1){c=(b/a)+1;a=a*c-b;b=b*c;printf("%d",c);if(a>1&&zc(a,b)){printf("%d\n",b/a);break;}elseif(a==1){

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论