图论 第四讲 经典算法Word下载.docx

上传人:b****8 文档编号:22821348 上传时间:2023-02-05 格式:DOCX 页数:19 大小:66.72KB
下载 相关 举报
图论 第四讲 经典算法Word下载.docx_第1页
第1页 / 共19页
图论 第四讲 经典算法Word下载.docx_第2页
第2页 / 共19页
图论 第四讲 经典算法Word下载.docx_第3页
第3页 / 共19页
图论 第四讲 经典算法Word下载.docx_第4页
第4页 / 共19页
图论 第四讲 经典算法Word下载.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

图论 第四讲 经典算法Word下载.docx

《图论 第四讲 经典算法Word下载.docx》由会员分享,可在线阅读,更多相关《图论 第四讲 经典算法Word下载.docx(19页珍藏版)》请在冰豆网上搜索。

图论 第四讲 经典算法Word下载.docx

begin

read(a[i,j]);

end;

{――――――读入图――――――――}

=2tondo

min:

=10000;

{设置最大值}

if(notv[i])and(a[1,i]<

>

0)and(a[1,i]<

min)then

找到距离最短的顶点

=a[1,i];

k:

=i;

end;

v[k]:

{设置以访问标

志}

更新最短权值

if(a[1,k]<

0)and(a[k,i]<

0)then

if(a[1,k]+a[k,i]<

a[1,i])or(a[1,i]=0)then

a[1,i]:

=a[1,k]+a[k,i];

writeln(a[1,i]);

{输出最短路径}

close(input);

close(output);

end.

BELLMAN算法

可以求负权的最短路径,可以判断有无负权回路。

对每一个顶点v,设置变量dis[v],意义是源点到v的最短路径。

对每一条边做v-1次松弛操作(不知道为什么叫这个名字)。

松弛操作:

对于边(u,v)如果dis[v]>

dis[u]+a[u,v],那么dis[v]=dis[u]+dis[u,v]。

programBELLMAN;

constmaxp=800;

{最大点数}

 

maxc=1450;

{最大边数}

max=300000;

typeedge=record{边}

a,b,d:

integer;

varpa:

array[1..maxp]ofinteger;

e:

array[1..maxc]ofedge;

{边表}

dis:

array[1..maxp]oflongint;

n,p,c,i,j,s:

tot,min:

longint;

flag:

boolean;

procedurerelax(a,b,d:

integer);

{松弛操作}

varz:

z:

=dis[a]+d;

ifz<

disthenbegin

=z;

=false;

fillchar(pa,sizeof(pa),0);

readln(p,c);

{C为边数p为点数}

fori:

=1tocdoreadln(e.a,e.b,e.d);

{a点和b点相通,长度为d}

s:

=1;

{单源最短的源}

=1topdodis[i]:

=max;

{初始化}

dis[s]:

=0;

主程序

repeat

=true;

=1tocdo

withedobegin

relax(a,b,d);

relax(b,a,d);

untilflag;

forI:

writeln(dis[i]);

SPFA算法

初始时将源加入队列.每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队.直到队列为空时算法结束.

SPFA算法的效率

时间复杂度一般认为是O(kE)

其中k是一个较大的常数,不好估计,但是可以看出SPFA算法效率应当是很高的

经验表明Dijkstra算法的堆优化要比SPFA快,但SPFA比普通的Dijkstra算法快.而SPFA算法可以处理负权的问题,而且比Dijkstra算法的堆优化的代码要容易实现,因此SPFA是一个很好的算法.

SPFA在处理稀疏图时效果比较不错在处理稠密图时可能没有Floyd好

programSPFA;

const

maxn=800;

{最大点}

maxq=maxn;

{最大边}

var

n,p,c:

map:

array[1..maxn,1..maxn]ofinteger;

{图}

array[1..maxn,1..maxn]oflongint;

{距离}

connect:

array[1..maxn,0..maxn]ofinteger;

{存图}

procedureinit;

i,x,y,distance:

初始化

fillchar(connect,sizeof(connect),0);

fillchar(map,sizeof(map),255);

readln(p,c);

{p为点C为边}

=1tocdo{读入}

readln(x,y,distance);

if(map[x,y]=1)or(distance<

map[x,y])then

map[x,y]:

=distance;

map[y,x]:

inc(connect[x,0]);

connect[x,connect[x,0]]:

=y;

inc(connect[y,0]);

connect[y,connect[y,0]]:

=x;

=1topdo

map[i,i]:

=0;

dis[i,i]:

procedurespfa(v0:

integer);

{vo源点}

i,now,open,close:

queue:

array[1..maxq]ofinteger;

{队列}

inqueue:

array[1..maxn]ofboolean;

{是否入队}

begin

fillchar(inqueue,sizeof(inqueue),0);

fillchar(queue,sizeof(queue),0);

fillchar(dis[v0],sizeof(dis[v0]),255);

dis[v0,v0]:

queue[1]:

=v0;

{vo入队}

inqueue[v0]:

open:

{队首}

close:

{队尾}

whileclose<

opendo

inc(open);

now:

=queue[open];

{出队}

inqueue[now]:

=false;

=1toconnect[now,0]do

if(dis[v0,connect[now,i]]=-1)or(dis[v0,now]+map[now,connect[now,i]]<

dis[v0,connect[now,i]])then{松弛操作}

dis[v0,connect[now,i]]:

=dis[v0,now]+map[now,connect[now,i]];

ifnotinqueue[connect[now,i]]then{入队}

inc(close);

queue[close]:

=connect[now,i];

inqueue[connect[now,i]]:

procedurework;

i,j:

=1tocowin[0]do

spfa(cowin[i]);

write(dis[i,j],'

'

writeln;

init;

work;

*弗洛伊德算法

*传递闭包。

也算是一种动态规划。

求任意两点最短路径。

枚举中间结点k,首结点i和尾结点j

programfloyd;

vara:

{存储图}

i,j,k,n:

if(i<

j)and(a[i,j]=0)thena[i,j]:

=maxint;

{数据初始化}

fork:

ifk<

ithen

if(k<

j)and(i<

j)and(a[i,j]>

a[i,k]+a[k,j])then

a[i,j]:

=a[i,k]+a[k,j];

writeln(任意两点最短路径);

对于floyd算法有很重要的应用。

在王建德的紫皮书上有传递闭包的很多用处。

在这里略过,但是一定要看一下,用传递闭包预处理数据是很有用,很重要的。

求拓扑序列

定义:

我自己不好说,看王建德的紫皮(P118)。

不懂定义就看不懂算法。

特点:

一个有向图结点的拓扑序列不是唯一的。

有环图不能拓扑排序。

求一个图的拓扑序列,判断有无回路。

欲处理时候很有用,有的搜索前很必要,比如《神经网络》

前提:

有向图

预处理数据的时候记下每一个点的入度,每次找出一个入度为0的点,删掉这个点,更新与它相邻的点的入度(入度减一)。

programtppv;

constmaxn=100;

array[1..maxn,1..maxn]ofbyte;

into:

array[1..maxn]ofbyte;

{每个结点的入度}

n,I,j,k:

byte;

{读入数据}

I,j:

assign(input,’input.in’);

assign(output,’output.out’);

readln(n);

fillchar(map,sizeof(map),0);

fillchar(into,sizeof(into),0);

whilenot(seekeof(input))do

readln(I,j);

map[I,j]:

inc(into[j]);

{有一条边连入,那么入度加1}

{输入数据}

j:

while(j<

=n)and(into[j]<

0)doinc(j);

{找到一个入度为0的点}

ifj<

=nthen{判断有无回路}

write(j,'

into[j]:

=255;

{删掉此点}

ifmap[j,k]=1thendec(into[k]);

{更新与j点连结的点的入度}

end

else

beginwriteln(‘存在回路,没有拓扑序列’);

halt;

end;

最小生成树

Prim算法

无向图或者强连通的有向图。

上图(B)和(C)都是(A)的生成树,生成树中各边的权值之和称为这棵生成树的代价,代价最小的生成树称为“最小代价生成树”。

已知所有边的权值,用最小的代价将所有的结点连起来。

设图的顶点集合V共有n个顶点,则算法如下:

1、设置一个顶点的集合S1和一个边的集合TE,S1和TE的初始状态均为空集;

2、选定图中的一个顶点K,从K开始生成最小代价生成树,将K加入到集合S1;

3、重复下列操作,直到选取了n-1条边:

选取一条权值最小的边(X,Y),其中X∈S1,not(Y∈S1)。

将顶点Y加入集合S1,边(X,Y)加入集合TE。

以上过程有些抽象……

下图给出了上图(A)的最小代价生成树的生成过程:

下面的程序代码是蓝皮书《数据结构与算法设计》的源程序。

对于Minclosd和closd数组的设计,可以参考此书,讲的还算明白。

programprim;

varminclosd,closd:

array[1..1000]ofinteger;

{设置minclosd,closd数组}

i,j,min,n,k:

{读入图注意数据的预处理}

minclosd[i]:

closd[i]:

{两数组初始化}

if(minclosd[i]<

min)and(minclosd[i]>

0)then

×

主要部分×

beginmin:

=minclosd[i];

=iend;

minclosd[k]:

if(a[k,i]<

minclosd[i])and(minclosd[i]<

beginminclosd[i]:

=a[k,i];

=k;

writeln(j,'

closd[j]);

Kruskal算法

也是求最小生成树。

设置一个b数组为已加入的顶点集合,每次找最小的一条边加入,直到所有的顶点加入集合。

与PRIM的不同:

Kruskal算法在森林中的两棵树之间添安全边;

Prim算法在单棵树上添安全边。

时间复杂度上要比PRIM低个常数,因为PRIM对点而KRUSKAL对边。

对于一个稠密的完全图,边要比点多。

相同点:

贪心,选择边权最小的安全边

优化:

并查集

programKruskal;

array[1..150,1..150]ofinteger;

b:

array[1..150]ofsetofbyte;

{一个集合}{最多只有255个,可以自己写数组}

i,j,k,l,n,min:

{=====================init====================================}

assign(input,'

assign(output,'

reset(input);

rewrite(output);

=1tondoread(a[i,j]);

readln;

{读入图}

{===========================find=============================}

procedurefind;

=1tondob[i]:

=[i];

if(a[i,j]>

0)and(a[i,j]<

min)and(not(jinb[i]))then

=a[i,j];

l:

=j;

{找到一条最小代价边,两结点是k和l}

b[k]:

=b[k]+b[l];

ifiinb[k]thenb[i]:

=b[k];

{更新已加入的边集合}

writeln(k,'

l);

untilb[1]=[1..n];

{==========================main=============================}

init;

find;

close(input);

close(output);

求图的的关键路径

此块内容我到现在也没有搞多清楚,大家一起看看,谁明白了交流一下(王建德紫皮书上有专门的一节)。

下面给出一种讲解。

关键路径问题

利用AOV网络,对其进行拓扑排序能对工程中活动的先后顺序作出安排。

但一个活动的完成总需要一定的时间,为了能估算出某个活动的开始时间,找出那些影响工程完成时间的最主要的活动,我们可以利用带权的有向网,图中的顶点表示一个活动结束(开始)的事件,图中的边表示活动,边上的权表示完成该活动所需要的时间。

这种用边表示活动的网络,称为AOE网络。

下图表示一个具有12个活动的AOE网络。

图中有8个顶点,分别表示事件(状态)0到7,其中,0表示开始状态,7表示结束状态;

边上的权表示完成该活动所需要的时间。

在实际中,AOE网络没有回路,存在唯一的入度为0的开始顶点,存在唯一的出度为0的结束顶点。

在AOE网络上,要研究的问题是完成整个工程至少需要多少时间,哪些活动是影响工程进度的关键。

在AOE网络中,有些活动可以并行地进行,所以,完成工程的最少时间是从开始顶点到结束顶点的最长路径的长度。

关键路径就是指从开始结点到完成结点的具有最大长度的路径。

关键路径上的活动称为关键活动。

关键路径的长度就是完成整个工程所需要的最短时间。

下面给出计算AOE网络的关键路径的数据结构和算法:

[数据结构]

adj[0..n-1,0..n-1]:

存放图的邻接矩阵;

est[0..n-1]:

est[i]存放事件i能够发生的最早时间,实际上是从开始顶点到i的最长路径的长度;

elt[0..n-1]:

elt[i]存放事件i能够发生的最迟时间,这是保证后续的事件j(i+1=<

j<

=n-1)能够按进度完成的保证,计算时,从n-1到i倒推,elt[i]等于est[n-1]减去顶点i到顶点n-1的最长路径的长度;

ast[n-1]:

ast[i]存放活动i能够发生的最早时间,如果活动i的边为<

j,k>

则ast[i]=elt[j];

alt[n-1]:

alt[i]存放活动i能够发生的最迟时间,如果活动i的边为<

,则alt[i]=elt[k]-adj[j,k];

[算法]

关键活

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1