算法设计与分析实验四.docx
《算法设计与分析实验四.docx》由会员分享,可在线阅读,更多相关《算法设计与分析实验四.docx(17页珍藏版)》请在冰豆网上搜索。
![算法设计与分析实验四.docx](https://file1.bdocx.com/fileroot1/2022-11/24/82484ba5-fe48-4ea8-8cc3-22da94e9f3aa/82484ba5-fe48-4ea8-8cc3-22da94e9f3aa1.gif)
算法设计与分析实验四
实验四:
贪心法
一、实验目的
1、掌握贪心算法的基本设计思想与原则
2、运用贪心法求解经典问题(验证性实验)
二、实验原理
1、优化问题
有n个输入,而它的解就由这n个输入满足某些事先给定的约束条件的某个子集组成,而把满足约束条件的子集称为该问题的可行解。
可行解一般来说是不唯一的。
那些使目标函数取极值(极大或极小)的可行解,称为最优解。
2、贪心法求优化问题
算法思想:
在贪心算法中采用逐步构造最优解的方法。
在每个阶段,都作出一个看上去最优的决策(在一定的标准下)。
决策一旦作出,就不可再更改。
作出贪心决策的依据称为贪心准则(greedycriterion)。
3、一般方法
1)根据题意,选取一种量度标准。
2)按这种量度标准对这n个输入排序
3)依次选择输入量加入部分解中。
如果当前这个输入量的加入,不满足约束条件,则不把此输入加到这部分解中。
procedureGREEDY(A,n)/*贪心法一般控制流程*/
//A(1:
n)包含n个输入//
solutions←φ//将解向量solution初始化为空/
fori←1tondo
x←SELECT(A)
ifFEASIBLE(solution,x)
thensolutions←UNION(solution,x)
endif
repeat
return(solution)
endGREEDY
三、实验要求
1、用C++/C完成算法设计和程序设计并上机调试通过。
2、撰写实验报告,提供实验结果和数据。
3、分析算法,要求给出具体的算法分析结果,包括时间复杂度和空间复杂度,并简要给出算法设计小结和心得。
四、实验内容
1、哈夫曼编码
设需要编码的字符集为{d1,d2,…,dn},它们出现的频率为{w1,w2,…,wn},应用哈夫曼树构造最短的不等长编码方案。
设需要编码的字符集为{d1,d2,…,dn},它们出现的频率为{w1,w2,…,wn},以d1,d2,…,dn作为叶子结点,w1,w2,…,wn作为叶子结点的权值,构造一棵哈夫曼编码树,规定哈夫曼编码树的左分支代表0,右分支代表1,则从根结点到每个叶子结点所经过的路径组成的0和1的序列便为该叶子结点对应字符的编码即为哈夫曼编码。
考虑到哈夫曼树中共有2n-1个结点,并且进行n-1次合并操作,为了便于选取根结点权值最小的二叉树以及合并操作,设置一个数组huffTree[2n-1]保存哈夫曼树中各结点的信息,数组元素的结点结构如下图所示。
将数组元素的结点类型定义为:
structelement
{
doubleweight;//字符出现的概率为实数
intlchild,rchild,parent;
};
建立哈夫曼树的算法如下:
Select函数用来在数组huffTree中选取两个权值最小的根结点,请读者自行完成。
根据已建立的哈夫曼树,规定哈夫曼树的左分支代表0,右分支代表1,则哈夫曼编码即是从根结点到每个叶子结点所经过的路径组成的0和1的序列。
算法如下:
2、背包问题
贪心算法:
procedureKNAPSACK(P,W,M,X,n)
//P(1:
n)和W(1;n)分别含有按
P(i)/W(i)≥P(i+1)/W(i+1)排序的n件物品的效益值
和重量。
M是背包的容量大小,而x(1:
n)是解向量
realP(1:
n),W(1:
n),X(1:
n),M,cu;
integeri,n;
X←0//将解向量初始化为零
cu←M//cu是背包剩余容量
fori←1tondo
ifW(i)>cuthenexitendif
X(i)←1
cu←cu-W(i)
repeat
ifi≤nthenX(i)←cu/W(i)
endif
endGREEDY-KNAPSACK
procedureprim(G,)
status←“unseen”//T为空
status[1]←“treenode”//将1放入T
foreachedge(1,w)do
status[w]←“fringe”//找到T的邻接点
dad[w]←1;//w通过1与T建立联系
dist[w]←weight(1,w)//w到T的距离
repeat
whilestatus[t]≠“treenode”do
pickafringeuwithmindist[w]//选取到T最近的节点
status[u]←“treenode”
foreachedge(u,w)do
修改w和T的关系
repeat
repeat
3、最小生成树的prim算法
PrimMST(G,T,r){
//求图G的以r为根的MST,结果放在T=(U,TE)中
InitCandidateSet(…);//初始化:
设置初始的轻边候选集,并置T=({r},¢)
for(k=0;k (u,v)=SelectLiShtEdge(…);//选取轻边(u,v);
T←T∪{(u,v)};//扩充T,即(u,v)涂红加入TE,蓝点v并人红点集U
ModifyCandidateSet(…);//根据新红点v调整候选轻边集
}
}
4、最短路径问题
最短路径问题是图论研究中的一个经典算法问题,旨在寻找图中两结点之间的最短路径。
给定带权有向图G=(V,E),其中每条边的权是非负实数。
另外,还给定V中的一个顶点,称为源。
现在要计算从源到所有其它各顶点的最短路长度。
这里路的长度是指路上各边权之和。
这个问题通常称为单源最短路径问题。
Dijkstra算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。
主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
创建两个表,OPEN,CLOSE。
OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。
1)访问路网中距离起始点最近且没有被检查过的点,把这个点放入OPEN组中等待检查。
2)从OPEN表中找出距起始点最近的点,找出这个点的所有子节点,把这个点放到CLOSE表中。
3)遍历考察这个点的子节点。
求出这些子节点距起始点的距离值,放子节点到OPEN表中。
4)重复第2和第3步,直到OPEN表为空,或找到目标点。
算法描述:
1)置集合S={2,3,...n},数组d
(1)=0,d(i)=W1->i(1,i之间存在边)or+无穷大(1.i之间不存在边)
2)在S中,令d(j)=min{d(i),i属于S},令S=S-{j},若S为空集则算法结束,否则转3)
3)对全部i属于S,如果存在边j->i,那么置d(i)=min{d(i),d(j)+Wj->i},转2)
Dijkstra算法思想为:
设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径,就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。
在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。
此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
算法具体步骤:
(1)初始时,S只包含源点,即S=,v的距离为0。
U包含除v外的其他顶点,U中顶点u距离为边上的权(若v与u有边)或)(若u不是v的出边邻接点)。
(2)从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
(3)以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u(uU)的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
(4)重复步骤
(2)和(3)直到所有顶点都包含在S中。
5、多机调度问题
要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
约定,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。
作业不能拆分成更小的子作业。
步骤:
1)把作业按加工所用的时间从大到小排序
2)如果作业数目比机器的数目少或相等,则直接把作业分配下去
3)如果作业数目比机器的数目多,则每台机器上先分配一个作业,如下的作业分配时,是选那个表头上s最小的链表加入新作业。
五、参考程序
2、背包问题贪心算法
#include
structgoodinfo
{
floatp;//物品效益
floatw;//物品重量
floatX;//物品该放的数量
intflag;//物品编号
};//物品信息结构体
voidInsertionsort(goodinfogoods[],intn)
{
intj,i;
for(j=2;j<=n;j++)
{
goods[0]=goods[j];
i=j-1;
while(goods[0].p>goods[i].p)
{
goods[i+1]=goods[i];
i--;
}
goods[i+1]=goods[0];
}
}//按物品效益,重量比值做升序排列
voidbag(goodinfogoods[],floatM,intn)
{
floatcu;
inti,j;
for(i=1;i<=n;i++)
goods[i].X=0;
cu=M;//背包剩余容量
for(i=1;i{
if(goods[i].w>cu)//当该物品重量大与剩余容量跳出
break;
goods[i].X=1;
cu=cu-goods[i].w;//确定背包新的剩余容量
}
if(i<=n)
goods[i].X=cu/goods[i].w;//该物品所要放的量
for(j=2;j<=n;j++)
{
goods[0]=goods[j];
i=j-1;
while(goods[0].flag{
goods[i+1]=goods[i];
i--;
}
goods[i+1]=goods[0];
}
cout<<"最优解为:
"<for(i=1;i<=n;i++)
{
cout<<"第"<
";
cout<}
}
voidmain()
{
cout<<"|--------运用贪心法解背包问题---------|"<cout<<"|-------------------------------------|"<intj;
intn;
floatM;
goodinfo*goods;//定义一个指针
while(j)
{
cout<<"请输入物品的总数量:
";
cin>>n;
goods=newstructgoodinfo[n+1];//
cout<<"请输入背包的最大容量:
";
cin>>M;
cout<inti;
for(i=1;i<=n;i++)
{goods[i].flag=i;
cout<<"请输入第"<
";
cin>>goods[i].w;
cout<<"请输入第"<
";
cin>>goods[i].p;
goods[i].p=goods[i].p/goods[i].w;//得出物品的效益,重量比
cout<}
Insertionsort(goods,n);
bag(goods,M,n);
cout<<"press<1>torunagian"<cout<<"press<0>toexit"<cin>>j;
}
}
3、最小生成树的prim算法
#include
#include
#include
#defineINFINITYINT_MAX
#defineMAX_VERTEX_NUM20
typedefintVRType;
typedefintInfoType;
typedefcharVerTexType;
typedefstructArcCell
{
VRTypeadj;
InfoType*info;
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedefstruct
{
VerTexTypevexs[MAX_VERTEX_NUM];
AdjMatrixarcs;
intvexnum,arcnum;
}MGraph;
typedefstruct
{
VerTexTypeadjvex;
VRTypelowcost;
}closedge[MAX_VERTEX_NUM];
voidCreateGraph(MGraph&G);
voidMiniSpanTree_PRIM(MGraphG,VerTexTypeu);
intLocateVex(MGraphG,VerTexTypeu);
intminimum(closedgeclose);
voidmain(void)
{
inti,j;
MGraphG;
CreateGraph(G);
for(i=0;i{
for(j=0;j{
cout<cout<<"";
}
cout<}
MiniSpanTree_PRIM(G,'a');
}
voidCreateGraph(MGraph&G)
{
intweigh;
inti,j=0,k=0;
charhand,tide;
cout<<"inputthenumberforvexnumandarcnum:
";
cin>>G.vexnum>>G.arcnum;
for(i=0;i{
for(j=0;jG.arcs[i][j].adj=88;
}
cout<cout<<"input"<";
for(i=0;icin>>G.vexs[i];
cout<cout<<"input"<"<j=0;
k=0;
for(i=0;i{
cout<
";
cin>>hand;
cin>>tide;
cin>>weigh;
while(hand!
=G.vexs[j])
j++;
while(tide!
=G.vexs[k])
k++;
G.arcs[j][k].adj=weigh;
G.arcs[k][j].adj=weigh;
j=0;
k=0;
cout<}
}
voidMiniSpanTree_PRIM(MGraphG,VerTexTypeu)
{
inti,j,k=0;
closedgeclose;
k=LocateVex(G,u);
for(j=0;j{
if(j!
=k)
{
close[j].adjvex=G.vexs[k];
close[j].lowcost=G.arcs[k][j].adj;
}
}
close[j].lowcost=88;
close[j].adjvex='\0';
close[k].lowcost=0;
close[k].adjvex=u;
for(i=1;i{
k=minimum(close);
cout<cout<<"---->";
cout<cout<close[k].lowcost=0;
for(j=0;j{
if(G.arcs[k][j].adj{
close[j].adjvex=G.vexs[k];
close[j].lowcost=G.arcs[k][j].adj;
}
}
}
}
intLocateVex(MGraphG,VerTexTypeu)
{
intk=0;
while(G.vexs[k++]==u)
returnk-1;
return0;
}
intminimum(closedgeclose)
{
intj1=0,client=88,j2;
while(close[j1].adjvex!
='\0')
{
if(client>close[j1].lowcost&&close[j1].lowcost!
=0)
{
client=close[j1].lowcost;
j2=j1;
}
j1++;
}
returnj2;
}
4、最短路径问题
#include
#include
usingnamespacestd;
constintMaxNum=1000000;//边权最大值
intn;//节点数目
intdist[501];//到节点1的最短路径值
boolstate[501];//节点被搜索过状态指示
intdata[501][501];//邻接矩阵
//查找权值最小的节点
intfindmin()
{
intminnode=0,min=MaxNum;
for(inti=1;i<=n;i++)
if((dist[i]state[i]))
{
min=dist[i];
minnode=i;
}
returnminnode;
}
intmain()
{
ifstreamin("dijkstra.in");
ofstreamout("dijkstra.out");
memset(state,0,sizeof(state));
in>>n;
for(intp=1;p<=n;p++)
for(intq=1;q<=n;q++)
{
in>>data[p][q];
if(data[p][q]==0)data[p][q]=MaxNum;
}
//初始化
for(inti=1;i<=n;i++)
dist[i]=data[1][i];
state[1]=true;
intdone=1;
while(done {
intnode=findmin();
if(node!
=0)
{
done++;//找到的点的数目加1
state[node]=true;//标记已经找到了从节点1到节点node的最短路径
for(inti=1;i<=n;i++)//更新还没有找到的点的路径值
if((dist[i]>dist[node]+data[node][i])&&(!
state[i]))
dist[i]=dist[node]+data[node][i];
}
elsebreak;
}
for(intk=1;k<=n;k++)
{
if(dist[k]==MaxNum)
out<<-1;
else
out< if(k==n)
out< else
out<<"";
}
in.close();
out.close();
return0;
}
5、多机调度问题
typedefstructJob
{
intID;//作业号
inttime;//作业所花费的时间
}Job;
typedefstructJobNode//作业链表的节点
{
intID;
inttime;
JobNode*next;
}JobNode,*pJobNode;
typedefstructHeader //链表的表头
{
ints;
pJobNodenext;
}Header,pHeader;
intSelectMin(Header*M,intm)
{
intk=0;
for(inti=1;i {
if(M[i].s }
returnk;
}