贪心法专题文档格式.docx
《贪心法专题文档格式.docx》由会员分享,可在线阅读,更多相关《贪心法专题文档格式.docx(13页珍藏版)》请在冰豆网上搜索。
B=A×
C+D
即:
B/A=C+D/A<
C+1
则:
A/B>
1/(C+1)
即1/(C+1)即为真分数A/B包含的最大埃及分数。
设E=C+1,由于
A/B–1/E=((A×
E)–B)/(B×
E)
则真分数减去最大埃及分数后,得到真分数((A×
E)–B)/B×
E,该真分数可能存在公因子,需要化简,可以将分子和分母同时除以最大公约数。
【算法】设函数EgyptFraction实现埃及分数问题,算法用伪代码描述如下:
算法7.1:
埃及分数EgyptFraction
输入:
真分数的分子A和分母B
输出:
最少的埃及分数之和
1.E=B/A+1;
2.输出1/E;
3.A=A*E–B;
B=B*E;
4.求A和B的最大公约数R,如果R不为1,则将A和B同时除以R;
5.如果A等于1,则输出1/B,算法结束;
否则转步骤1重复执行;
例2TSP问题
【问题】TSP问题是指旅行家要旅行n个城市,要求各个城市经历且仅经历一次然后回到出发城市,并要求所走的路程最短。
【想法1】TSP问题的贪心策略可以采用最近邻点策略:
从任意城市出发,每次在没有到过的城市中选择最近的一个,直到经过了所有的城市,最后回到出发城市。
如图7.1(a)所示是一个无向图的代价矩阵,从顶点1出发,按照最近邻点的贪心策略,得到的路径是1→4→3→5→2→1,总代价是14,求解过程如图7.1(b)~(f)所示。
需要说明的是,用最近邻点贪心策略求解TSP问题所得的结果不一定是最优解,例如,图7.1(a)中从顶点1出发的最优解是1→2→5→4→3→1,总代价只有13。
当图中顶点个数较多并且各边的代价分布比较均匀时,最近邻点策略可以给出较好的近似解,不过,这个近似解以何种程度近似于最优解,却难以保证。
例如,在图7.1中,如果增大边(2,1)的代价,则总代价只好随之增加,没有选择的余地。
【算法1】设图G中n个顶点的编号为{1,2,…,n},cij表示顶点i到顶点j的代价(1≤i,j≤n),集合V存储图的顶点,集合P存储经过的边,从顶点w出发采用最近邻点策略求解TSP问题的算法如下:
算法7.2:
最近邻点策略求解TSP问题
无向带权图G=(V,E)
回路长度TSPLength
1.初始化:
P={};
TSPLength=0;
2.u=w;
V=V-{w};
3.循环直到集合P中包含n-1条边
3.1查找与顶点u邻接的最小代价边(u,v)并且v属于集合V;
3.2P=P+{(u,v)};
V=V-{v};
TSPLength=TSPLength+cuv;
3.3输出经过的路径uv,u=v,转步骤3继续求解;
4.输出TSPLength+cuw;
例3图着色问题
【问题】给定无向连通图G=(V,E),图着色问题(graphcoloringproblem)求图G的最小色数k,使得用k种颜色对G中的顶点着色,可使任意两个相邻顶点着不同颜色。
例如,图7.3所示的无向图可以只用两种颜色着色,将顶点1、3和4着一种颜色,将顶点2和顶点5着另外一种颜色。
【想法】假定k个颜色的集合为{1,2,…,k}。
一种显然的贪心策略是选择一种颜色,用该颜色为尽可能多的顶点着色,具体地,选取颜色1,依次考察图中的未被着色的每个顶点,如果某顶点可以用颜色1着色,换言之,该顶点的邻接点都还未被着色,则用颜色1为该顶点着色;
再选择颜色2,依次考察图中的未被着色的每个顶点,如果某顶点着颜色2与其相邻顶点的着色不发生冲突,则用颜色2为该顶点着色;
如果还有未着色的顶点,则选取颜色3并为尽可能多的顶点着色,依此类推。
需要说明的是,贪心法求解图着色问题得到的不一定是最优解。
考虑一个具有2n个顶点的无向图,顶点的编号从1到2n,当i是奇数时,顶点i与除了顶点i+1之外的其他所有编号为偶数的顶点邻接,当i是偶数时,顶点i与除了顶点i-1之外的其他所有编号为奇数的顶点邻接,这样的图称为二部图(bipartitegraph)。
在二部图中,顶点可以分成两个集合V1(编号为奇数的顶点集合)和V2(编号为偶数的顶点集合),并且每一条边都连接V1中的一个顶点和V2中的一个顶点。
图7.4所示就是一个具有8个顶点的二部图。
显然,二部图只用两种颜色就可以完成着色,例如,可以将奇数顶点全部着成颜色1,将偶数顶点全部着成颜色2。
如果贪心法以1,3,…,2n-1,2,4,…,2n的顺序为二部图着色,则算法可以得到这个最优解,但是如果贪心法以1,2,…,n的自然顺序为二部图着色,则算法找到的是一个需要n种颜色的解。
【算法】设数组color[n]表示顶点的着色情况,贪心法求解图着色问题的算法如下:
算法7.4:
贪心法求解图着色问题
无向连通图G=(V,E)
最小色数k
1.所有顶点置未着色状态:
2.颜色k初始化为0;
3.循环直到所有顶点均着色
3.1取下一种颜色k++;
3.2依次考察所有顶点:
3.2.1若顶点i已着色,则转步骤3.2,考察下一个顶点;
3.2.2若顶点i着颜色k不冲突,则color[i]=k;
4.输出各顶点的着色;
例4最小生成树问题——prim算法
【问题】设G=(V,E)是一个无向连通网,求G的最小生成树。
最小生成树(minimalspanningtree)是各边的权值之和最小的生成树。
【想法】最小生成树问题的贪心策略可以采用最近顶点策略:
任选一个顶点,并以此建立生成树的根结点,每一步的贪心选择是把不在生成树中的最近顶点添加到生成树中。
Prim算法就应用了这个贪心策略,它使生成树以一种自然的方式生长,即从任意顶点开始,每一步为这棵树添加一个分枝,直到生成树中包含全部顶点。
设最小生成树T=(U,TE),初始时U={u0}(u0为任意顶点),TE={}。
显然,Prim算法的关键是如何找到连接U和V-U的最短边来扩充生成树T。
对于每一个不在当前生成树中的顶点v∈V-U,必须知道它连接生成树的最短边信息。
所以,对不在当前生成树中的顶点v∈V-U,需要保存两个信息:
lowcost[v]表示顶点v到生成树中所有顶点的最短边;
adjvex[v]表示该最短边在生成树中的顶点。
例如,对于图7.5(a)所示连通网,图7.5(b)~(g)给出了从顶点A出发,用Prim算法构造最小生成树的过程。
【算法】设置数组shortEdge[n]表示候选最短边集,数组元素包括adjvex和lowcost两个域,分别表示候选最短边的邻接点和权值,例如,式7-1表明候选最短边(vi,vk)的权值为w,其中vi∈V-U,vk∈U。
(式7-1)
假设从顶点w出发构造最小生成树,则初始时,U={w},且shortEdge[w].lowcost=0,表示顶点w已加入集合U中,数组元素shortEdge[i].adjvex=0,shortEdge[i].lowcost=(w,i)的权值(1≤i≤n-1)。
然后在数组shortEdge[n]中不断选取最小权值shortEdge[k].lowcost,将shortEdge[k].lowcost置为0,表示顶点k已加入集合U中。
由于顶点k从集合V-U进入集合U后,候选最短边集发生了变化,依据式7-2更新数组shortEdge的内容:
(式7-2)
设图G中顶点的编号为0~n-1,Prim算法如下:
算法7.5:
Prim算法
无向连通网G=(V,E),起始点w
最小生成树
1.初始化辅助数组shortEdge;
2.U={w};
输出顶点w;
3.重复执行下列操作n-1次
3.1在lowcost中选取最短边,取adjvex中对应的顶点序号k;
3.2输出顶点k和对应的权值;
3.3U=U+{k};
3.4调整数组shortEdge;
例5最小生成树问题——Kruskal算法
【想法】最小生成树问题的贪心策略可以采用最短边策略:
设最小生成树的边集为TE,最短边策略从TE={}开始,每一次贪心选择都是在边集E中选取最短边(u,v),如果边(u,v)加入集合TE中不产生回路,则将边(u,v)加入边集TE中,并将它在集合E中删去。
Kruskal算法就应用了这个贪心策略,它使生成树以一种随意的方式生长,先让森林中的树木随意生长,每生长一次就将两棵树合并,到最后合并成一棵树。
Kruskal算法对图7.6(a)所示无向连通网构造最小生成树的过程如图7.6(b)~(f)所示,其中,粗边表示已加入边集TE中。
12
26
19
25
17
(d)(e)(f)
图7.6Kruskal方法构造最小生成树的过程
【算法2】设图G中顶点的编号为0~n-1,Kruskal算法如下:
算法7.6:
Kruskal算法
无向连通网G=(V,E)
U=V;
TE={};
2.循环直到T中的连通分量个数为1
2.1在E中寻找最短边(u,v);
2.2如果顶点u、v位于T的两个不同连通分量,则
2.2.1将边(u,v)并入TE;
2.2.2将这两个连通分量合为一个;
2.3E=E-{(u,v)};
例6背包问题
【问题】给定n个物品和一个容量为C的背包,物品i的重量是wi,其价值为vi,背包问题(knapsackproblem)是如何选择装入背包的物品,使得装入背包中物品的总价值最大。
注意和0/1背包问题的区别,在背包问题中,可以将某种物品的一部分装入背包中,但不可以重复装入。
【想法】用贪心法求解背包问题的关键是如何选定贪心策略,使得按照一定的顺序选择每个物品,并尽可能的装入背包,直到背包装满。
至少有三种看似合理的贪心策略:
(1)选择价值最大的物品,因为这可以尽可能快地增加背包的总价值。
但是,虽然每一步选择获得了背包价值的极大增长,但背包容量却可能消耗得太快,使得装入背包的物品个数减少,从而不能保证目标函数达到最大。
(2)选择重量最轻的物品,因为这可以装入尽可能多的物品,从而增加背包的总价值。
但是,虽然每一步选择使背包的容量消耗得慢了,但背包的价值却没能保证迅速增长,从而不能保证目标函数达到最大。
(3)以上两种贪心策略或者只考虑背包价值的增长,或者只考虑背包容量的消耗,而为了求得背包问题的最优解,需要在背包价值增长和背包容量消耗两者之间寻找平衡。
正确的贪心策略是选择单位重量价值最大的物品。
【算法】设背包容量为C,共有n个物品,物品重量存放在数组w[n]中,价值存放在数组v[n]中,问题的解存放在数组x[n]中,贪心法求解背包问题的算法如下:
算法7.7:
贪心法求解背包问题
背包容量C,物品重量w[n],物品价值v[n]
数组x[n]
1.改变数组w和v的排列顺序,使其按单位重量价值v[i]/w[i]降序排列;
2.将数组x[n]初始化为0;
3.i=1;
4.循环直到(w[i]>
C)
4.1将第i个物品放入背包:
x[i]=1;
4.2C=C-w[i];
4.3i++;
5.x[i]=C/w[i];
例7活动安排问题
【问题】设有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相容。
活动安排问题(activityarrangementproblem)要求在所给的活动集合中选出最大的相容活动子集。
【想法】贪心法求解活动安排问题的关键是如何选择贪心策略,使得按照一定的顺序选择相容活动,并能够安排尽量多的活动。
至少有两种看似合理的贪心策略:
(1)最早开始时间:
这样可以增大资源的利用率。
(2)最早结束时间:
这样可以使下一个活动尽早开始。
由于活动占用资源的时间没有限制,因此,后一种贪心选择更为合理。
直观上,按这种策略选择相容活动可以为未安排的活动留下尽可能多的时间,也就是说,这种贪心选择的目的是使剩余时间段极大化,以便安排尽可能多的相容活动。
为了在每一次贪心选择时快速查找具有最早结束时间的相容活动,可以将n个活动按结束时间非减序排列。
这样,贪心选择时取当前活动集合中结束时间最早的活动就归结为取当前活动集合中排在最前面的活动。
例如,设有11个活动等待安排,这些活动按结束时间的非减序排列如表7.1所示。
表7.111个活动的开始时间和结束时间
i
1
2
3
4
5
6
7
8
9
10
11
si
fi
13
14
贪心法求解活动安排问题的过程如图7.9所示,其中阴影长条表示该活动已加入解集合中,空白长条表示该活动是当前正在检查相容性的活动。
首先选择活动1加入解集合,因为活动1具有最早结束时间;
活动2和活动3与活动1不相容,所以舍弃他们;
活动4与活动1相容,因此将活动4加入解集合;
然后在剩下的活动中找与活动4相容并具有最早结束时间的活动,依此类推。
最终被选定的活动集合为{1,4,8,11}。
【算法】设有n个活动等待安排,si表示活动i的起始时间,fi表示活动i的结束时间(1≤i≤n),集合B存放问题的解,即选定的活动集合,算法如下:
算法7.8:
活动安排问题
n个活动的开始时间{s1,s2,…,sn}和结束时间{f1,f2,…,fn}
选定的活动集合
1.对{f1,f2,…,fn}按非减序排序,同时相应地调整{s1,s2,…,sn};
2.最优解中包含活动1:
B={1};
3.j=集合B中最后结束的活动,初始时j=1;
4.循环变量i从2~n依次考察每一个活动:
4.1如果(si>
=fj)则
4.1.1B=B+{j};
4.1.2j=i;
4.2i++;
例8多机调度问题
【问题】设有n个独立的作业{1,2,…,n},由m台相同的机器{M1,M2,…,Mm}进行加工处理,作业i所需的处理时间为ti(1≤i≤n),每个作业均可在任何一台机器上加工处理,但不可间断、拆分。
多机调度问题(multi-machineschedulingproblem)要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
【想法】贪心法求解多机调度问题的贪心策略是最长处理时间作业优先,即把处理时间最长的作业分配给最先空闲的机器,这样可以保证处理时间长的作业优先处理,从而在整体上获得尽可能短的处理时间。
按照最长处理时间作业优先的贪心策略,当m≥n时,只要将机器i的[0,ti)时间区间分配给作业i即可;
当m<n时,首先将n个作业按其所需的处理时间从大到小排序,然后依此顺序将作业分配给最先空闲的处理机。
例如,设7个独立作业{1,2,3,4,5,6,7}由3台机器{M1,M2,M3}加工处理,各作业所需的处理时间分别为{2,14,4,16,6,5,3},首先将这7个作业按处理时间从大到小排序,则作业{4,2,5,6,3,7,1}的处理时间为{16,14,6,5,4,3,2},贪心法产生的作业调度如图7.10所示,具体过程如下:
(1)按照最长处理时间作业优先的贪心策略,将作业4分配给机器M1,则机器M1将在时间16后空闲;
将作业2分配给机器M2,则机器M2将在时间14后空闲;
将作业5分配给机器M3,则机器M3将在时间6后空闲,至此,三台机器均已分配作业;
(2)在三台机器中空闲最早的是机器M3,将作业6分配给机器M3,并且机器M3将在时间6+5=11后空闲;
(3)在三台机器中空闲最早的是机器M3,将作业3分配给机器M3,并且机器M3将在时间11+4=15后空闲;
(4)在三台机器中空闲最早的是机器M2,将作业7分配给机器M2,并且机器M2将在时间14+3=17后空闲;
(5)在三台机器中空闲最早的是机器M3,将作业1分配给机器M3,并且机器M3将在时间15+2=17后空闲。
最后得到所需加工的最短时间为17。
【算法】设n个作业的处理时间存储在数组t[n]中,m台机器的空闲时间存储在数组d[m]中,集合数组S[m]存储每台机器所处理的作业,其中集合S[i]表示机器i所处理的作业,贪心法求解多机调度问题的算法如下:
算法7.9:
多机调度问题
n个作业的处理时间t[n],m台机器的空闲时间d[m]
每台机器所处理的作业S[m]
1.将数组t[n]由大到小排序,对应的作业序号存储在数组p[n]中;
2.将数组d[m]初始化为0;
3.for(i=0;
i<
m;
i++)
3.1将前m个作业分配给m个机器:
S[i]={p[i]};
3.2d[i]=t[i];
4.for(i=m;
n;
4.1j=数组d[m]中最小值对应的下标;
4.2将作业i分配给最先空闲的机器j:
S[j]=S[j]+{p[i]};
4.3机器j将在d[j]后空闲:
d[j]=d[j]+t[i];