当前位置:文档之家› 网络流算法和PASCAL程序

网络流算法和PASCAL程序

网络流算法和PASCAL程序
网络流算法和PASCAL程序

网络流算法及其应用

网络流

网络流 network flows网络流的理论和应用在不断发展,出现了具有增益的流、多终端流、多商品流以及网络流的分解与合成等新课题。网络流的应用已遍及通讯、运输、电力、工程规划、任务分派、设备更新以及计算机辅助设计等众多领域。

目录

定义

图论中的一种理论与方法,研究网络上的一类最优化问题。1955年,T.E. 哈里斯在研究铁路最大通量时首先提出在一个给定的网络上寻求两点间最大运输量的问题。1956年,L.R. 福特和 D.R. 富尔克森等人给出了解决这类问题的算法,从而建立了网络流理论。所谓网络或容量网络指的是一个连通的赋权有向图 D=(V、E、C),其中V 是该图的顶点集,E是有向边(即弧)集,C是弧上的容量。此外顶点集中包括一个起点和一个终点。网络上的流就是由起点流向终点的可行流,这是定义在网络上的非负函数,它一方面受到容量的限制,另一方面除去起点和终点以外,在所有中途点要求保持流入量和流出量是平衡的。如果把下图看作一个公路网,顶点v1 (v6)

表示6座城镇,每条边上的权数表示两城镇间的公路长度。现在要问:若从起点v1将物资运送到终点v6去,应选择那条路线才能使总运输距离最短???这样一类问题称为最短路问题。如果把上图看作一个输油管道网, v1 表示发送点,v6表示接收点,其他点表示中转站,各边的权数

寻找通路的时候可以用DFS,BFS最短路等算法。就这两者来说,BFS要比DFS快得多,但是编码量也会相应上一个数量级。

增广路方法可以解决最大流问题,然而它有一个不可避免的缺陷,就是在极端情况下每次只能将流扩大1(假设容量、流为整数),这样会造成性能上的很大问题,解决这个问题有一个复杂得多的算法,就是预推进算法。

2、push label,直译为“预推进”算法。

3、压入与重标记(Push-Relabel)算法

除了用各种方法在剩余网络中不断找增广路(augmenting)的

Ford-Fulkerson系的算法外,还有一种求最大流的算法被称为压入与重标记(Push-Relabel)算法。它的基本操作有:压入,作用于一条边,将边的始点的预流尽可能多的压向终点;重标记,作用于一个点,将它的高度(也就是label)设为所有邻接点的高度的最小值加一。Push-Relabel系的算法普遍要比Ford-Fulkerson系的算法快,但是缺点是相对难以理解。

Relabel-to-Front使用一个链表保存溢出顶点,用Discharge操作不断使溢出顶点不再溢出。Discharge的操作过程是:若找不到可被压入的临边,则重标记,否则对临边压入,直至点不再溢出。算法的主过程是:首先将源点出发的所有边充满,然后将除源和汇外的所有顶点保存在一个链表里,从链表头开始进行Discharge,如果完成后顶点的高度有所增加,则将这个顶点置于链表的头部,对下一个顶点开始Discharge。

Relabel-to-Front算法的时间复杂度是O(V^3),还有一个叫Highest Label Preflow Push的算法复杂度据说是O(V^2*E^0.5)。我研究了一下HLPP,感觉它和Relabel-to-Front本质上没有区别,因为Relabel-to-Front 每次前移的都是高度最高的顶点,所以也相当于每次选择最高的标号进行更新。还有一个感觉也会很好实现的算法是使用队列维护溢出顶点,每次对pop出来的顶点discharge,出现了新的溢出顶点时入队。

Push-Relabel类的算法有一个名为gap heuristic的优化,就是当存在一个整数0k的顶点v 做更新,若它小于V+1就置为V+1。

cpp程序:

#include

const long maxn=1000+10;

const long maxm=440000+100;

const long maxnum=100000000;

long

n,m,s=0,tot=0,list[maxn],p[maxn],g[maxn],gg[maxn],dis[maxn];

bool mark[maxn];

struct node

{

long k,f,c,next,g;

}d[maxm<<1];

void insert(long u,long v,long c)

{

d[tot].k=v;

d[tot].f=0;

d[tot].c=c;

d[tot].next=g;

g=tot;

d[tot].g=tot+1;

tot++;

d[tot].k=u;

d[tot].f=0;

d[tot].c=0;

d[tot].next=g[v];

g[v]=tot;

d[tot].g=tot-1;

tot++;

}

void bfs()

{

long x,u,v,h,t;

for (long i=0;i

dis=0;

h=0;

t=1;

list[1]=0;

编辑本段最小费用最大流

一.Ford和Fulkerson迭加算法.

基本思路:把各条弧上单位流量的费用看成某种长度,用求解最短路问题的方法确定一条自V1至Vn的最短路;在将这条最短路作为可扩充路,用求解最大流问题的方法将其上的流量增至最大可能值;而这条最短路上的流量增加后,其上各条弧的单位流量的费用要重新确定,如此多次迭代,最终得到最小费用最大流.

迭加算法:

1) 给定目标流量F或∞,给定最小费用的初始可行流=0

2) 若V(f)=F,停止,f为最小费用流;否则转(3).

3) 构造相应的新的费用有向图W(fij),在W(fij)寻找Vs到Vt的最小费用有向路P(最短路),沿P增加流f的流量直到F,转(2);若不存在从Vs到Vt的最小费用的有向路P,停止.f就是最小费用最大流.

具体解题步骤:

设图中双线所示路径为最短路径,费用有向图为W(fij).

在图(a)中给出零流 f,在图(b)中找到最小费用有向路,修改图(a)中的可行流,δ=min{4,3,5}=3,得图(c),再做出(c)的调整容量图,再构造相应的新的最小费用有向路得图(d), 修改图(c)中的可行流, δ=min{1,1,2,2}=1,得图(e),以此类推,一直得到图(h),在图(h)中以无最小费用有向路,停止,

经计算:

图(h)中最小费用=1*4+3*3+2*4+4*1+1*1+4*2+1*1+3*1=38

图(g)中最大流=5

最大流问题仅注意网络流的流通能力,没有考虑流通的费用。实际上费用因素是很重要的。例如在交通运输问题中,往往要求在完成运输任务的前提下,寻求一个使总运输费用最省的运输方案,这就是最小费用流问题。如果只考虑单位货物的运输费用,那么这个问题就变成最短路问题。由此可见,最短路问题是最小费用流问题的基础。现已有一系列求最短路的成功方法。最小费用流(或最小费用最大流)问题,可以交替使用求解最大流和最短路两种方法,通过迭代得到解决。

二.圈算法:

1) 利用Ford和Fulkson标号算法找出流量为F(<=最大流)的流f.

2) 构造f对应的调整容量的流网络N'(f).

3) 搜索N'(f)中的负费用有向图C(Floyd算法),若没有则停止,否则转

(4).

4) 在C上找出最大的循环流,并加到N上去,同时修改N'(F)中C的容量,转(3).

具体解题步骤:

利用Ford和Fulkson标号算法,得网络的最大流F=5,见图(a),由图(a)构造相应的调整容量的流网络(b),图(b)中不存在负费用有向图,故停止.经计算:

图(b)中最小费用= 4*1+3*1+1*1+3*3+4*2+1*1+4*1+2*4=38

图(a)中最大流为F=5

Type

szbest=record

v,l:integer;

end;

szmap=record

c,f,w:integer;

end;

Var

map:array[0..1000,0..1000] of szmap;

best:array[0..1000] of szbest;

i,j,ans,n,k,a,b,c,d,s,t:integer;

Function find:boolean;

var i,j:integer;

flag:boolean;

begin

for i:=1 to n do best.v:=maxint;

best[s].v:=0;

repeat

flag:=true;

for i:=1 to n do

if best.v

for j:=1 to n do

if (map[i,j].f

begin

best[j].v:=best.v+map[i,j].w;

best[j].l:=i; flag:=false;

end;

until flag;

if best[t].v

else exit(false);

end;

Procedure updata;

var i,j:integer;

begin

i:=t;

while i<>s do

begin

j:=best.l;

inc(map[j,i].f);

map[i,j].f:=-map[j,i].f;

i:=j;

end;

inc(ans,best[t].v);

end;

begin {main}

assign(input,'g4.in');reset(input);

assign(output,'g4.out');rewrite(output);

readln(n);

repeat

readln(a,b,c,d);

if a+b+c+d>0 then

begin

map[a,b].c:=c; map[b,a].c:=0;

map[a,b].w:=d; map[b,a].w:=-d;

end;

until a+b+c+d=0;

s:=1; t:=n; ans:=0;

while find do updata;

writeln(ans);

for i:=1 to n do

for j:=1 to n do

if map[i,j].f>0 then

writeln(i,' ',j,' ',map[i,j].w,'*',map[i,j].f);

close(input); close(output);

end.

三,ZKW费用流

费用流是网络流的一个很重要的组成部分,也是非常有用的一种图论模型,关于费用流的算法,流传比较广的主要是消圈和增广路算法,而近来炒得沸沸扬扬的ZKW算法也是一种非常优秀的算法,这里我就谈谈我对此算法的一些理解。

此算法是由ZKW大牛创立,主要思想仍然是找增广路,只是有了一些优化在里边。原来我们找增广路主要是依靠最短路算法,如SPFA。因此此算法的时间复杂度主要就取决于增广的次数和每次增广的耗费。由于每一次找增广路是都是重新算一遍,这样似乎显得有些浪费,如果我们能够缩短找增广路的时间,那必定会大大地优化算法。

值得注意的是,在寻求最短路得过程中,设dis[i]为i到起点的距离,对于每一条由i引向j的边,必有dis[j]<=dis[i]+map[i][j];既然满足这样的恒等式,我们就可以借用KM算法的调整思想来寻求最短路,每次只走dis[j]=dis[i]+map[i][j]的路径,一旦不存在到达终点的路径,就扫描每一条边,找到最小的距离增加值,使得有至少一条新边被加入相等子图。

算法流程如下:

1.将dis数组清零,表示当前的相等子图内只有起点。

(如果存在负权边,必须要用spfa跑一遍,初始化出dis数组,下面的第二题就是这样的例子)。

2.深搜,如果到达终点,全部回退更改流量,再进行步骤2;否则,转3。

3.修改dis的值,如果无法修改,结束程序,已经找到的答案,反之转2。

编辑本段有上下界的最大流

v上面讨论的网络流都只对每条弧都限定了上界(其实其下界可以看成0),现在给每条弧加上一个下界限制Aij (即必须满足Aij≤fij)。

v弧上数字对第一个是上界,第二个是下界。若是撇开下界不看,此图的最大流如图(a)所示,流量是6;但若是加入了下界的限制,它的最大流量就只有5了。

编辑本段网络流算法

一、网络流的基本概念

先来看一个实例。

现在想将一些物资从S运抵T,必须经过一些中转站。连接中转站的是公路,每条公路都有最大运载量。如下图:

每条弧代表一条公路,弧上的数表示该公路的最大运载量。最多能将多少货物从S运抵T?

这是一个典型的网络流模型。为了解答此题,我们先了解网络流的有关定义和概念。

若有向图G=(V,E)满足下列条件:

1、有且仅有一个顶点S,它的入度为零,即d-(S) = 0,这个顶点S 便称为源点,或称为发点。

2、有且仅有一个顶点T,它的出度为零,即d+(T) = 0,这个顶点T 便称为汇点,或称为收点。

3、每一条弧都有非负数,叫做该边的容量。边(vi, vj)的容量用cij 表示。

则称之为网络流图,记为G = (V, E, C)

譬如图5-1就是一个网络流图。

1.可行流

对于网络流图G,每一条弧(i,j)都给定一个非负数fij,这一组数满足下列三条件时称为这网络的可行流,用f表示它。

1、每一条弧(i,j)有fij≤cij。

2、除源点S和汇点T以外的所有的点vi,恒有:

该等式说明中间点vi的流量守恒,输入与输出量相等。

3、对于源点S和汇点T有:

这里V(f)表示该可行流f的流量。

例如对图5-1而言,它的一个可行流如下:

流量V(f) = 5。

2.可改进路

给定一个可行流f=。若fij = cij,称为饱和弧;否则称为非饱和弧。若fij = 0,称为零流弧;否则称为非零流弧。

定义一条道路P,起点是S、终点是T。把P上所有与P方向一致的弧定义为正向弧,正向弧的全体记为P+;把P上所有与P方向相悖的弧定义为反向弧,反向弧的全体记为P-。

譬如在图5-1中,P = (S, V1, V2, V3, V4, T),那么

P+ = {, , , }

P- = {}

给定一个可行流f,P是从S到T的一条道路,如果满足:

那么就称P是f的一条可改进路。(有些书上又称:可增广轨)之所以称作“可改进”,是因为可改进路上弧的流量通过一定的规则修改,可以令整个流量放大。具体方法下一节会重点介绍,此不赘述。

3.割切

要解决网络最大流问题,必须先学习割切的概念和有关知识。

G = (V, E, C)是已知的网络流图,设U是V的一个子集,W = V\U,满足S U,T W。即U、W把V分成两个不相交的集合,且源点和汇点分属不同的集合。

对于弧尾在U,弧头在W的弧所构成的集合称之为割切,用(U,W)表示。把割切(U,W)中所有弧的容量之和叫做此割切的容量,记为C(U,W),即:

例如图5-1中,令U = {S, V1},则W = {V2, V3, V4, T},那么

C(U, W) = + + +=8+4+4+1=17 定理:对于已知的网络流图,设任意一可行流为f,任意一割切为(U, W),必有:V(f) ≤ C(U, W)。

通俗简明的讲:“最大流小于等于最小割”。这是“流理论”里最基础最重要的定理。整个“流”的理论系统都是在这个定理上建立起来的,必须特别重视。

下面我们给出证明。

网络流、可改进路、割切都是基础的概念,应该扎实掌握。它们三者之间乍一看似乎风马牛不相干,其实内在联系是十分紧密的。

二、求最大流

何谓最大流?首先它必须是一个可行流;其次,它的流量必须达到最大。这样的流就称为最大流。譬如对图5-1而言,它的最大流如下:下面探讨如何求得最大流。

在定义“可改进路”概念时,提到可以通过一定规则修改“可改进路”上弧的流量,可以使得总流量放大。下面我们就具体看一看是什么“规则”。

对可改进路P上的弧,分为两种情况讨论:

第一种情况:∈P+,可以令fij增加一个常数delta。必须满足fij + delta ≤ ci j,即delta ≤ cij – fij。

第二种情况:∈P-,可以令fij减少一个常数delta。必须满足fij - delta ≥ 0,即delta ≤ fij

根据以上分析可以得出delta的计算公式:

因为P+的每条弧都是非饱和弧,P-的每条弧都是非零流弧,所以delta > 0。

容易证明,按照如此规则修正流量,既可以使所有中间点都满足“流量守恒”(即输入量等于输出量),又可以使得总的流量有所增加(因为delta > 0)。

因此我们对于任意的可行流f,只要在f中能找到可改进路,那么必然可以将f改造成为流量更大的一个可行流。我们要求的是最大流,现在的问题是:倘若在f中找不到可改进路,是不是f就一定是最大流呢?

答案是肯定的。下面我们给出证明。

定理1 可行流f是最大流的充分必要条件是:f中不存在可改进路。

证明:

首先证明必要性:已知最大流f,求证f中不存在可改进路。

若最大流f中存在可改进路P,那么可以根据一定规则(详见上文)修改P中弧的流量。可以将f的流量放大,这与f是最大流矛盾。故必要性得证。

再证明充分性:已知流f,并且f中不存在可改进路,求证f是最大流。

我们定义顶点集合U, W如下:

(a)S∈U,

(b)若x∈U,且fxy

若x∈U,且fyx>0,则y∈U。

(这实际上就是可改进路的构造规则)

(c) W = V \ U。

由于f中不存在可改进路,所以T∈W;又S∈U,所以U、W是一个割切(U, W)。

按照U的定义,若x∈U,y∈W,则fxy = cxy。若x∈W,y∈U,则fxy = 0。

所以,

又因v(f)≤C(U,W)

所以f是最大流。得证。

根据充分性证明中的有关结论,我们可以得到另外一条重要定理:

最大流最小割定理:最大流等于最小割,即max V(f) = min C(U, W)。

至此,我们可以轻松设计出求最大流的算法:

step 1. 令所有弧的流量为0,从而构造一个流量为0的可行流f(称作零流)。

step 2. 若f中找不到可改进路则转step 5;否则找到任意一条可改进路P。

step 3. 根据P求delta。

step 4. 以delta为改进量,更新可行流f。转step 2。

step 5. 算法结束。此时的f即为最大流。

三、最小费用最大流

1.问题的模型

流最重要的应用是尽可能多的分流物资,这也就是我们已经研究过的最大流问题。然而实际生活中,最大配置方案肯定不止一种,一旦有了选择的余地,费用的因素就自然参与到决策中来。

图5-8是一个最简单的例子:弧上标的两个数字第一个是容量,第二个是费用。这里的费用是单位流量的花费,譬如fs1=4,所需花费为3*4=12。

容易看出,此图的最大流(流量是8)为:fs1 = f1t = 5, fs2 = f2t = 3。所以它的费用是:3*5+4*5+7*3+2*3 = 62。

一般的,设有带费用的网络流图G = (V, E, C, W),每条弧对应两个非负整数Cij、Wij,表示该弧的容量和费用。若流f满足:

(a) 流量V(f)最大。

(b) 满足a的前提下,流的费用Cost(f) = 最小。

就称f是网络流图G的最小费用最大流。

2.算法设计

我们模仿求最大流的算法,找可改进路来求最小费用最大流。

设P是流f的可改进路,定义为P的费用(为什么如此定义?)。如果P是关于f的可改进路中费用最小的,就称P是f的最小费用可改进路。

求最小费用最大流的基本思想是贪心法。即:对于流f,每次选择最小费用可改进路进行改进,直到不存在可改进路为止。这样的得到的最大流必然是费用最小的。

算法可描述为:

step 1. 令f为零流。

step 2. 若无可改进路,转step 5;否则找到最小费用可改进路,设为P。

step 3. 根据P求delta(改进量)。

step 4. 放大f。转step 2。

step 5. 算法结束。此时的f即最小费用最大流。

至于算法的正确性,可以从理论上证明。读者可自己思考或查阅有关运筹学资料。

2.最小费用可改进路的求解

求“最小费用可改进路”是求最小费用最大流算法的关键之所在,下面我们探讨求解的方法。

设带费用的网络流图G = (V, E, C, W),它的一个可行流是f。我们构造带权有向图B = (V’, E’),其中:

1、V’ = V。

2、若∈E,fij∈E’,权为Wij。

∈E,fij>0,那么∈E’,权为-Wij。

显然,B中从S到T的每一条道路都对应关于f的一条可改进路;反之,关于f的每条可改进路也能对应B中从S到T的一条路径。即两者存在一一映射的逻辑关系。

故若B中不存在从S到T的路径,则f必然没有可改进路;不然,B中从S到T的最短路径即为f的最小费用可改进路。

现在的问题变成:给定带权有向图B = (V’, E’),求从S到T的一条最短路径。

考虑到图中存在权值为负数的弧,不能采用Dijkstra算法;Floyd算法的效率又不尽如人意——所以,这里采用一种折衷的算法:迭代法。

设Short[k]表示从S到k顶点的最短路径长度;从S到顶点k的最短路径中,顶点k的前趋记为Last[k]。那么迭代算法描述如下:(为了便于描述,令n = |V’|,S的编号为0,T的编号为n+1)

0。?+∞(1≤k≤n+1),Short[0] ?step 1. 令Short[k] step 2. 遍历每一条弧。若Short[k] + < Short[k] +?Short[j],则令Short[j] ?,同时Last[j] k。倘不存在任何一条弧满足此条件则转step 4。

step 3. 转step 2.

step 4. 算法结束。若Short[n + 1]= +∞,则不存在从S到T的路径;否则可以根据Last记录的有关信息得到最短路径。

一次迭代算法的时间复杂度为O(kn2),其中k是一个不大于n的变量。在费用流的求解过程中,k大部分情况下都远小于n。

3.思维发散与探索

1)可改进路费用:“递增!递增?”

设f从零流到最大流共被改进了k次,每i次选择的可改进路的费用为pi,那么会不会有p1≤p2≤p3≤……≤pk呢?

2)迭代法:“小心死循环!嘿嘿……”

迭代法会出现死循环吗?也就是说,构造的带权有向图B中会存在负回路吗?

3)费用:“你在乎我是负数吗?”

网络流图中的费用可以小于零吗?

4)容量:“我管的可不仅是弧。”

网络流图中的“容量”都是对弧而言的,但若是给每个顶点也加上一个容量限制:即通过此顶点的流量的上限;任务仍然是求从S到T的最小费用最大流。你能解决吗?

四、有上下界的最大流

上面讨论的网络流都只对每条弧都限定了上界(其实其下界可以看成0),现在给每条弧加上一个下界限制Aij(即必须满足Aij≤fij)。

例如图5-9:

弧上数字对第一个是上界,第二个是下界。若是撇开下界不看,此图的最大流如图5-10(a)所示,流量是6;但若是加入了下界的限制,它的最大流量就只有5了,具体方案见图5-10(b)。

那么有上下界的网络最大流怎么求呢?

一种自然的想法是去掉下界,将其转化为只含上界的网络流图。这种美好的愿望是可以实现的。具体方法如下:

设原网络流图为G = (V, E, C, A),构造不含下界的网络流图G’ = (V’, E’, C’):

1、V’ = V∪{S’, T’}

2、对每个顶点x,令,若h-(x)≠0,就添加一条弧,其上界为h-(x)。

3、对每个顶点x,令,若h+(x)≠0,就添加一条弧,其上界为h+(x)。

4、对于任何∈E,都有∈E’,其上界C’ij = Cij – Aij。

5、新增∈E’,其上界CTS = +∞。

在G’中以S’为源点、T’为汇点求得最大流f’。若f’中从S’发出的任意一条弧是非饱和弧,则原网络流图没有可行流。否则可得原图的一个可行流f = f’ + A,即所有的fij = f’ij + Aij。(其正确性很容易证明,留给读者完成)

然后再求可改进路(反向弧必须满足fij≥Aij,而非fij≥0),不断放大f,直到求出最大流。

我们看到,上几节所讨论的一种可行网络流实际上是{Aij = 0}的一种特殊网络流,这里提出的模型更一般化了。解决一般化的复杂问题,我们采取的思路是将其转化为特殊的简单问题,加以研究、推广,进而解决。这是一种重要的基本思想:化归——简单有效。基于这种思想,请读者自行思考解决:

1、有上下界的最小流。

2、有上下界的最小费用最大流。

五、多源点、多汇点的最大流

已知网络流图有n个源点S1、S2、……、Sn,m个汇点T1、T2、……、Tm,,求该图的最大流。这样的问题称为多源点、多汇点最大流。

它的解决很简单:

1、增设一个“超级源”S’,对每个源点Si,新增弧,容量为无穷大。

2、增设一个“超级汇”T’,对每个汇点Ti,新增弧,容量为无穷大。

3、以S’为源点、T’为汇点求最大流f。

4、将f中的S’和T’去掉,即为原图的最大流。

算法正确性显然。

六、顶点有容量限制的最大流

上一节已经提出了这个问题,即对于进出每个顶点的流量也规定一个上限,这样的最大流如何求?

既然我们已经解决了“边限制”问题,现在何不把“点限制”问题转化为“边限制”呢?具体办法如下:

1、对除源点和汇点之外的每个顶点i拆分成两个顶点i’和i’’。新增一条弧,其容量为点i的流量限制。

2、对于原图中的弧,我们将其变换成

3、对变换后的图求最大流即可。

这里我们又一次运用到了化归的思想:将未知的“点限制”问题转化为已知的“边限制”问题。

七、网络流与二部图的匹配

{二部图和匹配的定义可参见本书专门介绍二部图匹配的章节}

设二部图为G = (X, Y, E)。

增设点S’,对于所有i∈X,新增弧,容量为1;增设点T’,对于所有i∈Y,新增一条弧,容量也为1。原图中所有的弧予以保留,容量均为+∞。对新构造出来的网络流图以S’为源点、T’为汇点求最大流:流量即为最大匹配数;若弧(i∈X,j∈Y)的流量非零,它就是一条匹配边。

二部图最大匹配问题解决。

那么二部图的最佳匹配问题又如何?

仍然按照上述方法构图。同时令原图中弧的费用保持不变;新增弧的费用置为0。然后以S’为源点、T’为汇点求最小费用最大流即可。最大流的费用即为原二部图最佳匹配的费用。

八、网络流的应用(略)

5.1 基本概念

在实际生活中有许多流量问题,例如在交通运输网络中的人流、车流、货物流,供水网络中的水流,金融系统中的现金流,通讯系统中的信息流,等等。50年代以福特(Ford)、富克逊(Fulkerson)为代表建立的“网络流理论”,是网络应用的重要组成部分。在最近的奥林匹克信息学竞赛中,利用网络流算法高效地解决问题已不是什么稀罕的事了。本节着重介绍最大流(包括最小费用)算法,并通过实际例子,讨论如何在问题的原型上建立—个网络流模型,然后用最大流算法高效地解决问题。

1.问题描述如图5-1所示是联结某产品地v1和销售地v4的交通网,每一弧(vi,vj)代表从vi到

vj的运输线,产品经这条弧由vi输送到vj,弧旁的数表示这条运输线的最大通过能力。产品经过交通网从v1到v4。现在要求制定一个运输方案使从v1到v4的产品数量最多。

图5-1 图5-2

2.网络与网络流

给一个有向图N=(V,E),在V中指定一点,称为源点(记为vs,和另一点,称为汇点(记为vt),其余的点叫中间点,对于E中每条弧(vi,vj)都对应一个正整数c(vi,vj)≥O(或简写成cij),称为f的容量,则赋权有向图N=(V,E,c,vs,vt)称为一个网络。如图5-1所给出的一个赋权有向图N就是一个网络,指定v1是源点,v4为汇点,弧旁的数字为cij。所谓网络上的流,是指定义在弧集合E上一个函数f={f(vi,vj)},并称f(vi,vj)为弧(vi,vj)上的流量(下面简记为fij)。如图5-2所示的网络N,弧上两个数,第一个数表示容量cij,第二个数表示流量fij。

3.可行流与最大流

在运输网络的实际问题中,我们可以看出,对于流有两个显然的要求:一是每个弧上的流量不能超过该弧的最大通过能力(即弧的容量);二是中间点的流量为0,源点的净流出量和汇点的净流入量必相等且为这个方案的总输送量。因此有:

(1)容量约束:0≤f ij≤c ij,(v i,v j)∈E,

(2)守恒条件

对于中间点:流入量=流出量;对于源点与汇点:源点的净流出量v s(f)=汇点的净流入量(-v t(f))的流f,称为网络N上的可行流,并将源点s的净流量称为流f的流值v(f)。

网络N中流值最大的流f*称为N的最大流。

4.可增广路径

所谓可增广路径,是指这条路径上的流可以修改,通过修改,使得整个网络的流值增大。

设f是一个可行流,P是从源点s到汇点t的一条路,若p满足下列条件:

(1)在p上的所有前向弧(vi→vj)都是非饱和弧,即0≤f ij

(2)在p上的所有后向弧(vi←vj)都是非零弧,即0

则称p为(关于可行流f的)一条可增广路径。

5.最大流定理

当且仅当不存在关于f*的增广路径,可行流f*为最大流。

5. 2 最大流算法

算法思想:最大流问题实际上是求一可行流{fij},使得v(f达到最大。若给了一个可行流f,只要判断N中有无关于f的增广路径,如果有增广路径,改进f,得到一个流量增大的新的可行流;如果没有增广路径,则得到最大流。

1.寻求最大流的标号法(Ford,Fulkerson)

从一个可行流(一般取零流)开始,不断进行以下的标号过程与调整过程,直到找不到关于f 的可增广路径为止。

(1)标号过程

在这个过程中,网络中的点分为已标号点和未标号点,已标号点又分为已检查和未检查两种。每个标号点的标号信息表示两个部分:第一标号表明它的标号从哪一点得到的,以便从vt开始反向追踪找出也增广路径;第二标号是为了表示该顶点是否已检查过。

标号开始时,给vs标上(s,0),这时vs是标号但末检查的点,其余都是未标号的点,记为(0,0)。

取一个标号而未检查的点vi,对于一切未标号的点vj:

A.对于弧(vi,vj),若fij

B.对于弧(vi,vj),若fji>0,则给vj标号(-vi,0),这时,vj点成为标号而未检查的点。

于是vi成为标号且已检查的点,将它的第二个标号记为1。重复上述步骤,一旦vt被标上号,表明得到一条从vi到vt的增广路径p,转入调整过程。

若所有标号都已检查过去,而标号过程进行不下去时,则算法结束,这时的可行流就是最大流。

(2)调整过程

从vt点开始,通过每个点的第一个标号,反向追踪,可找出增广路径P。例如设vt的第一标号为vk(或-vk),则弧(vk,vt)(或相应地(vt,vk))是p上弧。接下来检查vk的第一标号,若为vi(或-vi),则找到(vi,vk)(或相应地(vk,vi))。再检查vi的第一标号,依此类推,直到vs为止。这时整个增广路径就找到了。在上述找增广路径的同时计算Q:

Q=min{min(cij-fij),minf*ij}

对流f进行如下的修改:

f'ij = fij+Q (vi,vj)∈P的前向弧的集合

f'ij = fij-Q (vi,vj)∈P的后向弧的集合

f'ij = f*ij (vi,vj)不属于P的集合

接着,清除所有标号,对新的可行流f’,重新进入标号过程。

例1:下图表示一个公路网,V1是某原材料产地,V6表示港口码头,每段路的通过能力(容量)如图上的各边上的数据,找一运输方案,使运输到码头的原材料最多?

程序如下:

Program Max_Stream;

Const Maxn=20;

type

nettype=record

C,F:integer;

end;

nodetype=record

L,P:integer;

end;

var

Lt:array[0..maxn] of nodetype;

G:A rray[0..Maxn,0..Maxn] of Nettype;

N,S,T:integer;

F:Text;

Procedure Init;{初始化过程,读人有向图,并设置流为0}

Var Fn :String;

I,J :Integer;

Begin

Write( 'Graph File = ' ); Readln(Fn);

Assign(F,Fn);

Reset(F);

Readln(F,N);

Fillchar(G,Sizeof(G) ,0);

Fillchar(Lt,Sizeof(Lt),0);

For I:=1 To N Do

For J:=1 To N Do Read(F,G[I,J].C);

Close(F);

End;

Function Find: Integer; {寻找已经标号未检查的顶点}

Var I: Integer;

Begin

I:=1;

While (I<=N) And Not((Lt[I].L<>0)And(Lt[I].P=0)) Do Inc(I); If I>N Then Find:= 0 Else Find:= I;

End;

Function Ford(Var A: Integer):Boolean;

Var {用标号法找增广路径,并求修改量A}

I,J,M,X:Integer;

Begin

Ford:=True;

Fillchar(Lt,Sizeof(Lt),0);

Lt[S].L:=S;

Repeat

I:= Find;

If i=0 Then Exit;

For J:=1 To N Do

If (Lt[J].L= 0)And((G[I,J].C<>0)or(G[J,I].C<>0)) Then

Begin

if (G[I,J].F

If (G[J,I].F>0) Then Lt[J].L:=-I;

End;

Lt[I].P:=1;

Until (Lt[T].L<>0);

M:=T;A:=Maxint;

Repeat

J:=M;M:=Abs(Lt[J].L);

If Lt[J].L<0 Then X:= G[J,M].F;

If Lt[J].L>0 Then X:= G[M,J].C- G[M,J].F;

If X

Until M= S;

Ford:=False;

End;

Procedure Change(A: Integer);{调整过程}

Var M, J: Integer;

Begin

M:= T;

Repeat

J:=M;M:=Abs(Lt[J].L);

If Lt[J].L<0 Then G[J,M].F:=G[J,M].F-A;

If Lt[J].L>0 Then G[M,J].F:=G[M,j].F+A; Until M=S;

End;

Procedure Print; {打印最大流及其方案}

VA R

I ,J: Integer;

Max: integer;

Begin

Max:=0;

For I:=1 To N DO

Begin

If G[I,T].F<>0 Then Max:= Max + G[I,T].F;

For J:= 1 To N Do

If G[I,J].F<>0 Then Writeln( I, '-> ' ,J,' ' ,G[I,J].F); End;

Writeln('The Max Stream=',Max);

End;

Procedure Process;{求最大流的主过程}

Var Del:Integer;

Success:Boolean;

Begin

S:=1;T:=N;

Repeat

Success:=Ford(Del);

If Success Then Print

Else Change(Del);

Until Success;

End;

Begin {Main Program}

Init;

Process;

End.

测试数据文件(zdl.txt)如下:

6

0 3 5 0 0 0

0 0 1 4 0 0

0 0 0 0 2 0

0 0 0 0 0 5

0 1 0 3 0 2

0 0 0 0 0 0

运行结果如下:

Graph File=zdl.txt

1->2 3

1->3 2

2->4 3

3->5 2

4->6 3

5->6 2

The Max Stream=5

5.3最小费用最大流及算法

上面的网络,可看作为辅送一般货物的运输网络,此时,最大流问题仅表明运输网络运输货物的能力,但没有考虑运送货物的费用。在实际问题中,运送同样数量货物的运输方案可能有多个,因此从中找一个输出费用最小的的方案是一个很重要的问题,这就是最小代价流所要讨论的内容。

1.最小费用最大流问题的模型

给定网络N=(V,E,c,w,s,t),每一弧(vi,vj)属于E上,除了已给容量c ij外,还给了一个单位流量的费用w(vi,vj)≥O(简记为w ij)。所谓最小费用最大流问题就是要求一个最大流f,使得流的总输送费用取最小值。

W(f)=∑w ij f ij

2.用对偶法解最小费用最大流问题

定义:已知网络N=(V,E,c,w,s,t),f是N上的一个可行流,p为vs到vt(关于流f)的可增广路径,称W(p)=∑w ij(p+)-∑w ij(p-)为路径p的费用。

若p*是从vs到vt所有可增广路径中费用最小的路径,则称p*为最小费用可增广路径。

定理:若f是流量为v(f)的最小费用流,p是关于f的从vs到vt的一条最小费用可增广路径,则f经过p调整流量Q得到新的可行流f’(记为f’=f+Q),一定是流量为v(f)+Q的可行流中的最小费用流。

3.对偶法的基本思路

①取f={0}

②寻找从vs到vt的一条最小费用可增广路径p。

若不存在p,则f为N中的最小费用最大流,算法结束。

若存在p,则用求最大流的方法将f调整成f*,使v(f*)=v(f)+Q,并将f*赋值给f,转②。

4.迭代法求最小费用可增广路径

在前一节中,我们已经知道了最大流的求法。在最小费用最大流的求解中,每次要找一条最小费用的增广路径,这也是与最大流求法唯一不同之处。于是,对于求最小费用最大流问题余下的问题是怎样寻求关于f的最小费用增广路径p。为此,我们构造一个赋权有向图b(f),它的顶点是原网络N中的顶点,而把N中每一条弧(vi,vj)变成两个相反方向的弧(vi,vj)和(vj,vi)。定义w(f)中的弧的权如下:

如果(f ij

如果(f ij>0),则b ji=-w ij;如果(f ij=c ij),则b ji=+oo

于是在网络N中找关于f的最小费用增广路径就等价于在赋权有向图b(f)中,寻求从vs到vt的最短路径。求最短路有三种经典算法,它们分别是Dijkstra算法、Floyd算法和迭代算法(ford算法)。由于在本问题中,赋权有向图b(f)中存在负权,故我们只能用后两种方法求最短路,其中对于本问题最高效的算法是迭代算法。为了程序的实现方便,我们只要对原网络做适当的调整。将原网络中的每条弧(vi,vj)变成两条相反的弧:

前向弧(vi,vj),其容量c ij和费用w ij不变,流量为f ij;

后向弧(vj,vi),其容量0和费用-w ij,流量为-f ij。

事实上,对于原网络的数据结构中,这些单元已存在,在用标号法求最大流时是闲置的。这样我们就不必产生关于流f的有向图b(f)。同时对判断(vi,vj)的流量可否改时,可以统一为判断是否“f ij0,则f ji=-f ij<0=c ji。

例2:求输送量最大且费用最小的运输方案?

如下图是一公路网,v1是仓库所在地(物资的起点),v5是某一工地(物资的终点)每条弧旁的两个数字分别表示某一时间内通过该段路的最多吨数和每吨物资通过该段路的费用。

程序如下:

{最小费用最大流参考程序}

program Maxflow_With_MinCost;

const

name1='flow.in';

name2='flow.out';

maxN=100;{最多顶点数}

type

Tbest=record {数组的结构}

value,fa:integer;

end;{到源点的最短距离,父辈}

Nettype=record

{网络邻接矩阵类型}

c,w,f:integer;

{弧的容量,单位通过量费用,流量}

end;

var

Net:array[1..maxN,1..maxN] Of Nettype;

{网络N的邻接矩阵}

n,s,t:integer;{顶点数,源点、汇点的编号}

Minw:integer;{最小总费用}

best:array[1..maxN] of Tbest;{求最短路时用的数组} procedure Init;{数据读人}

var inf:text;

a,b,c,d:integer;

begin

fillchar(Net,sizeof(Net),0);

Minw:=0;

assign(inf,name1);

reset(inf);

readln(inf,n);

s:=1;t:=n;{源点为1,汇点n}

repeat

readln(inf,a,b,c,d);

if a+b+c+d>0 then

begin

Net[a,b].c:=c;{给弧(a,b)赋容量c}

Net[b,a].c:=0;{给相反弧(b,a)赋容量0}

Net[a,b].w:=d;{给弧(a,b)赋费用d}

Net[b,a].w:=-d;{给相反孤(b,a)赋费用-d}

end;

until a+b+c+d=0;

close(inf);

end;

function Find_Path:boolean;

{求最小费用增广路函数,若best[t].value<>MaxInt,

则找到增广路,函数返回true,否则返回false}

var Quit:Boolean;

i,j:Integer;

begin

for i:=1 to n do best[i].value:=Maxint;best[s].value:=0; {寻求最小费用增广路径前,给best数组赋初值} repeat

Quit:=True;

for i:=1 to n do

if best[i].Value < Maxint then

for j:=1 to n do

if (Net[i,j].f < Net[i,j].c) and

(best[i].value + Net[i,j].w <

best[j].value) then

begin

best[j].value:=best[i].value + Net[i,j].w;

best[j].fa := i;

Quit:= False;

Pascal基础知识

一、初识Pascal语言 一、Pascal 语言概述 Pascal 语言是一种算法语言,它是瑞士苏黎世联邦工业大学的Niklaus Wirth教授于1968年设计完成的,1971年正式发表。1975年对Pascal 语言进行了修改,作为“标准PASCAL语言”。 Pascal 语言是一种结构化的程序设计语言,可以用来编写应用程序。它又是一种系统程序设计语言,可以用来编写顺序型的系统软件(如编译程序)。它的功能强、编译程序简单。 二、Pascal 语言的特点 Pascal语言有以下几个主要的特点: ⒈它是结构化的语言。Pascal语言提供了直接实现三种基本结构的语句以及定义“过程”和“函数”的功能。可以方便地书写出结构化程序。在编写程序时可以完全不使用GOTO语句和标号。这就易于保证程序的正确性和易读性。Pascal语言强调的是可靠性、易于验证性、概念的清晰性和实现的简化。在结构化这一点上,比其它(如 BASIC,FORTRAN77)更好一些。 ⒉有丰富的数据类型。Pascal提供了整数、实型、字符型、布尔型、枚举型、子界型、数组类型、集合类型、记录类型、和文件类型和指针类型。⒊能适用于数值运算和非数值运算领域。PASCAL的功能较强,能广泛应用于各种领域。PASCAL语言还可以用于辅助设计,实现计算机绘图功能。 ⒋ PASCAL程序的书写格式比较自由。PASCAL允许一行写多个语句,一个语句可以分写在多行上,这样就可以使PASCAL程序写得格式优美,便于阅读。 三、Pascal语言程序的基本结构 程序设计语言都有着一组自己的记号和规则。PASCAL语言必须采用其本身所规定的记号和规则来编写程序。下面我们首先来了解Pascal语言的程序基本结构。 Pascal语言的程序结构为: 程序首部 标号说明语句 常量定义语句 类型定义语句程序的说明部分 变量说明语句 函数和过程说明语句分程序 程序体程序的执行部分 先看一个简单的PASCAL程序: program exam1(input,output); var r,s,c:real; begin readln(r); c:=3.14*2*r; s:=3.14*r*r; writeln(c,s) end.

pascal 过程与函数教程

第十二课过程与函数 前面我们曾经学习了程序设计中的三种基本控制结构(顺序、分支、循环)。用它们可以组成任何程序。但在应用中,还经常用到子程序结构。 通常,在程序设计中,我们会发现一些程序段在程序的不同地方反复出现,此时可以将这些程序段作为相对独立的整体,用一个标识符给它起一个名字,凡是程序中出现该程序段的地方,只要简单地写上其标识符即可。这样的程序段,我们称之为子程序。 子程序的使用不仅缩短了程序,节省了内存空间及减少了程序的编译时间,而且有利于结构化程序设计。因为一个复杂的问题总可将其分解成若干个子问题来解决,如果子问题依然很复杂,还可以将它继续分解,直到每个子问题都是一个具有独立任务的模块。这样编制的程序结构清晰,逻辑关系明确,无论是编写、阅读、调试还是修改,都会带来极大的好处。在一个程序中可以只有主程序而没有子程序(本章以前都是如此),但不能没有主程序,也就是说不能单独执行子程序。pascal中子程序有两种形式:函数和过程。 一、函数 在此之前,我们曾经介绍并使用了pascal提供的各种标准函数,如ABS,SUCC等等,这些函数为我们编写程序提供了很大的方便。但这些函数只是常用的基本函数,编程时经常需要自定义一些函数。 (一)函数的说明 在pascal中,函数也遵循先说明后使用的规则,在程序中,函数的说明放在调用该函数的程序(主程序或其它子程序)的说明部分。函数的结构主程序的结构很相似。 函数定义的一般格式: function <函数名> (<形式参数表>):<类型>; {函数首部} 说明: ①函数由首部与函数体两部分组成。 ②函数首部以关键字function开头。 ③函数名是用户自定义的标识符。 ④函数的类型也就是函数值的类型,所求得的函数值通过函数名传回调用它的程序。可见,函数的作用一般是为了求得一个值。 ⑤形式参数简称形参,形参即函数的自变量。自变量的初值来源于函数调用。在函数中,形参一般格式如下: 变量名表1:类型标识符1;变量名表2:类型标识符2;…;变量名表n:类型标识符n 可见形参表相当于变量说明,对函数自变量进行说明,但应特别注意:此处只能使用类型标识符,而不能直接使用类型。 ⑥当缺省形参表(当然要同时省去一对括号)时,称为无参函数。 ⑦函数体与程序体基本相似,由说明部分和执行部分组成。 ⑧函数体中的说明部分用来对本函数使用的标号、常量、类型、变量、子程序加以说明,这些量只在本函数内有效。 ⑨函数体的执行部分由begin开头,end结束,中间有若干用分号隔开的语句,只是end后应跟分号,不能像程序那样用句号"."。 ⑩在函数体的执行部分,至少应该给函数名赋一次值,以使在函数执行结束后把函数值带回调用程序。 (二)函数的调用

pascal算法题

算法应用综合测试(二) (作答时间:3小时) 说明:1、作题前先在D盘新建一个文件夹,以自已的“学校+姓名”命名 2、各程序的源文件名、输入输出文件见题目 第一题杨辉三角(yh.pas) 【问题描述】 输出杨辉三角第n行。 【输入数据】 一个整数n,表示要输出杨辉三角的第n行。 【输出数据】 一行,杨辉三角的第n行,两个数之间用空格隔开。 【数据范围】 对于30%数据,0

某一天,tenshi看了一本趣味数学书,上面提到了亲和数:定义数对 (x,y) 为亲和数对当且仅仅当x、y为不同正整数,且x、y各自的所有非自身正因子之和等于另一个数。例如 (220,284) 和 (280,224) 都是亲和数对,因为: 220的所有非自身正因子之和为:1 + 2 + 4 + 5 + 10 + 11 + 20 + 22 + 44 + 55 + 110 = 284 284的所有非自身正因子之和为:1 + 2 + 4 + 71 + 142 = 220 数对 (x,y ) 跟 (y,x) 被认为是同一数对,所以我们只考虑 x

pascal 基本算法

基本算法模块 对于NOIP,基础是相当重要的,在3个小时之内做完4道题,那么就要求我们有相当快的速度。特别是对于一些简单的、常用的算法模块,一定要要熟练掌握并灵活运用。由于NOIP是一个比较基础的比赛,因此基本算法的掌握尤为重要,所以要求能够把这些基本的模块快速、准确的移植到不同的程序中,才能在稳中取胜。 基本算法模块中最重要的是基本程序框架,也就是说,要养成适合于自己的程序风格,这样对于程序编写的速度与程序的准确度都有较大的提高。

模块目录 一、排序 1.选择排序 2.插入排序 3.冒泡排序 4.快速排序 5.堆排序 6.归并排序 7.线性时间排序二、高精度 1.高精度比较 2.高精度加法 3.高精度减法 4.单精度乘法 5.高精度乘法 6.单精度除法 7.高精度除法 8.进制转换 三、数论 1.欧几里德算法 2.扩展欧几里德 3.求最小公倍数 4.求解线形同余方程 5.素数的判断 6.素数的生成 四、排列组合 1.排列生成算法 2.组合生成算法 3.排列按序生成法 4.排列字典序生成法五、图论 1.图的读入 2.深度优先搜索 3.广度优先搜索 4.强连同分量 5.拓扑排序 6.最小生成树 7.最短路径 六、背包问题 1.装满背包 2.一维价值最大背包 3.二位价值最大背包

一、排序算法 var a:array[1..maxn]of longint;——排序对象 1.选择排序——Select_sort procedure select_sort; begin for i:=1to n-1do for j:=i+1to n do if a[i]>a[j]then begin temp:=a[i];a[i]:=a[j];a[j]:=temp;end; end; 2.插入排序——Insert_sort procedure insert_sort; begin for i:=2to n do begin key:=a[i];j:=i-1; while(key0)do begin a[j+1]:=a[j];dec(j);end; a[j+1]:=key; end; end; 3.冒泡排序——Bubble_sort procedure bubble_sort; begin for i:=1to n-1do for j:=n downto i+1do if a[j]x do dec(j);{找右边比他小的} if i<=j then{交换} begin temp:=a[i];a[i]:=a[j];a[j]:=temp;

Tarjan算法 Pascal语言描述

Tarjan算法Pascal语言描述 Tarjan算法Pascal语言描述 TonyShaw 那天做了个2-sat题,里面牵扯到求有向图的强连通分量,我这么弱,显然不会,于是从网上找求有向图的强连通分量的方法,有一个是DFS两遍,同时建原图与补图,算法名字是B???忘掉了,反正当时同时看见了Tarjan算法。鉴于我对于Tarjan的略微崇拜,于是想先学一下Tarjan。写这篇文章的原因在于,我在网上没有找到Pascal语言描述的程序,同时一些关于这个算法的解释不是很清楚,所以我想写一下,算是我对该算法理解的总结,也算是为其他要学习该算法的人提供点无用的参照吧。 算法思想:从一个点开始,进行深度优先遍历,同时记录到达该点的时间(dfn记录到达i 点的时间),和该点能直接或间接到达的点中的最早的时间(low记录这个值,其中low的 初始值等于dfn)。(如图。假设我们从1开始DFS,那么到达1的时间为1,到达2的时间为2,到达3的时间为3。同时,点1能直接或间接到达的点中,最小时间为1,点2能通过3间接到达点1,所以点2可到达最早的点时间为1,点3可以直接到达点1,故点3到达的最早的点的时间为1。)。对于每一个没有被遍历到的点A,如果从当前点有一条到未遍历点A的有向边,则遍历到A,同时将点A入栈,时间戳+1并用dfn[a]记录到达点A的时间,枚举从A发出的每一条边,如果该边指向的点没有被访问过,那么继续dfs,回溯后low[a]:=min(low[a],low[j])(其中j为A可以到达的点。)如果该点已经访问过并且该点仍在栈里,那么low[a]=min(low[a],dfn[j])。 解释:若点j没有被访问过,那么回溯后low[j]就是j能到达最早点,a能到达的最早点当然就是a本身能到达的最早点,或者a通过j间接到达的最早点。若点j已经被访问过,那么low[j]必然没有被回溯所更新。所以low[a]就等于a目前能到达的最小点或a直接到达点j 时点j的访问时间。注意:两个句子中的“或”其实指的是两者的最小值。那么如果我们回溯

PASCAL语法解释

我是在高一接触pascal语言,因为参加NOI的需要,顺理成章的要使用Turbo Pascal来写程序了。半年后,我开始想着如何编写Windows程序,又理所当然的找上Delphi。初见Delphi,除了begin,end 让我觉得倍感亲切外,Object Pascal里的增加的面向对象的语法却让我很是吃惊,当时的我可根本不懂什么叫面向过程,面向对象;最可恶的是,国内那些教育家们,除了会拿着清华的那本精简的不能再精简的pascal教材照本宣科外,似乎再也没有什么实质性的工作了,传说中的《Turbo Pascal大全》更是无处可寻,所以关于unit,interface这些Delphi里随处可见的关键字我也很不明白。所幸,其后不久,我得到一本名为《计算机反病毒技术》的书,里面统统都是用Turbo Pascal编写的源代码,通过它我迅速明白了早已存在于Turbo Pascal中unit,interface等关键字的含义和用法,又以Delphi中的Help文档为扶手,开始蹒跚学步了。 印象中,国内Delphi作家似乎更偏爱编写应用实例类的技术书籍,至于语法这种东西,没有几个人愿意多去涉及,即使书中必须谈及,也是寥寥数笔,匆匆带过,或者干脆与某本书类似。对Object Pascal语法讲解最好,最权威的恐怕就算《Delphi5开发人员指南》了,这本书至今也是备受推崇的。但与如今泛滥的C++书籍相比,Delphi仍然逊色许多,也难怪很多新手特别是从来没有接触过pascal语言的新手,在学习Object Pascal时会遇到不少困难。自己的感觉是:在从Turbo Pascal向Delphi过渡的过程中,由于没有正确的指引,走了很多弯路;由于没有正确的桥梁,必须要一步实现大跨越。所以,在这里,我提出自己曾经遇见的沟壑,路标性给出我自己的认识和总结,希望给入门的同学们一些帮助。我不打算详细介绍语法知识,并假设你已经有一点pascal语言和面向对象概念的基础。要想学习相关详细知识,我推荐各位一定要阅读《Delphi开发人员指南》和Delphi Help文档中的相关章节。 ●记录体和类 习惯了在一个Program模块内写完所有面向过程代码的我,有几天的时间一直未能彻底明白在非Unit模块中,非继承的自定义类的框架,语法是如何的,VCL源代码虽然经典,却过于繁杂,不能让我迅速掌握根本,我需要一个最简单又最能说明问题,完整的可运行的代码,苦于无处寻求答案,只好亲自动手,探索对应关系,终成其下两段代码。 program TP;{本代码在Turbo Pascal7.0下编译通过} type MyRecord=record {...} end; var MR:MyRecord; procedure Procedure1; begin {Procedure1Code} end; {===========main===========} begin {以这个begin为标志,主程序开始,其作用相当于C/C++中的main函数} Procedure1; end.

数据结构经典算法试题

1.假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。【北京大学1998 三、1 (5分)】 LinkedList Union(LinkedList la,lb) { pa=la->next; pb=lb->next; la->next=null; while(pa!=null && pb!=null) ∥当两链表均不为空时作 if(pa->data<=pb->data) { r=pa->next; pa->next=la->next; ∥将pa结点链于结果表中,同时逆置。 la->next=pa; pa=r; } else {r=pb->next; pb->next=la->next; ∥将pb结点链于结果表中,同时逆置。 la->next=pb; pb=r; } while(pa!=null) ∥将la表的剩余部分链入结果表,并逆置。 {r=pa->next; pa->next=la->next; la->next=pa; pa=r; } while(pb!=null) {r=pb->next; pb->next=la->next; la->next=pb; pb=r; } }

1)设有两个无头结点的单链表,头指针分别为ha,hb,链中有数据域data,链域next,两链表的数据都按递增序存放,现要求将hb表归到ha表中,且归并后ha仍递增序,归并中ha表中已有的数据若hb中也有,则hb中的数据不归并到ha中,hb的链表在算法中不允许破坏。【南京理工大学1997 四、3(15分)】 LinkedList Union(LinkedList ha, hb) ∥ha和hb是两个无头结点的数据域值递增有序的单链表,本算法将hb中并不出现在ha中的数据合并到ha中,合并中不能破坏hb链表。{LinkedList la; la=(LinkedList)malloc(sizeof(LNode)); la->next=ha; pa=ha; pb=hb; pre=la; while(pa&&pb) if(pa->datadata) ∥处理ha中数据 {pre->next=pa;pre=pa;pa=pa->next;} else if(pa->data>pb->data) ∥处理hb中数据。 {r=(LinkedList)malloc(sizeof(LNode)); r->data=pb->data; pre->next=r; pre=r; pb=pb->next; } Else ∥处理pa- >data=pb->data; { pre->next=pa; pre=pa; pa=pa->next; ∥两结点数据相等时,只将ha的数据链入。 pb=pb->next; } if(pa!=null)pre->next=pa; ∥将两链表中剩余部分链入结果链表。 else pre->next=pb; free(la); }

算法与程序设计经典例题

第一节选择题 选择题是一种各学科均使用的常见题型,它的结构由指令性语言、题干和选择支三个部分组成。 指令性语言:通常在大题号后面,本大题所有小题的前面,用括号括起来的部分;一般有三个方面的 内容:一是本大题包含的小题数目、每小题的分值和本大题的总分;二是指明每个小题中正确答案的数量; 三是每小题的计分方法。 题干:是指每一小题中叙述考查内容的不完整(加上某个选择支就能完整)的句子。 选择支:是题干后面的备选答案。 在信息技术会考试题中均采用“四选一”型的单项选择题,即一道选择题的四个选择支中,有且只有 一个正确选项。 选择题形式多样,结构灵活,可考查知识的覆盖面广,能比较全面地考察考生的基本知识和基本操作 技能,而且选择题答案具有确定性,阅卷方便,考试信度和效度高等特点,但选择题只在限定的备选项中 选出正确选项,其考核功能有一定的局限性,对考生的创新能力的培养有不同程度的影响。 选择题的解法很多,主要可以从直接法和间接法两方面着手。 一、直接法 直接法是指运用所学知识或根据操作经验,直接从题干出发,经过回忆、计算、比较,得出结论后与 备选答案进行对照,选出正确的选项。 【例1】以下主要用于制作网页的软件是 (A)Excel(B)Linux(C)FrontPage(D)PowerPoint (浙江省2006年会考试题)分析目前每一位考生所使用的网页制作软件不多,绝大部分都在使用(C)。 【例2】下列主要用来输入音频信息的设备是 (A)键盘(B)显示器(C)话筒(D)扫描仪 (A)销售盗版软件(B)下载免费软件(C)购买正版软件(D)发布共享软件 (浙江省2002年会考试题)分析本题可以根据计算机使用道德及计算机软件保护条例等知识直接得到答案:(A)。 【例6】有如下Visual Basic程序段: If x>0 Then y=2 End If 它的控制结构属于 (A)循环结构(B)树型结构(C)分支结构(D)顺序结构 (浙江省2004年会考试题)分析作为信息技术基础的内容,要求能看懂程序的基本控制结构及简单程序的阅读理解,如果在简 单程序中有If … then …语句,则此种控制结构一定是分支结构。本题答案为(C)。 【例7】下列说法中正确的是 (A)图像通常有位图和点阵图两种表示形式 (B)用Windows系统中的“画图”程序画出来的图形就是矢量图 (C)矢量图经过移动、缩放、旋转和扭曲等变换后清晰度不会发生变化 (D)图像文件中只记录生成图的算法和图上的某些特征点,数据量较小 分析本题可以根据图像与图形、位图与矢量图等基本概念直接得到答案(C) 【例8】一位同学用数码相机拍了一些照片,他想对这些照片上的人物和背景进行重新组合,以获得 最佳效果,你可以建议他采用的软件是 (A)Flash(B)Photoshop(C)画图程序(D)Powerpoint

pascal.时间复杂度的计算

时间复杂度的计算 当我们评价一个算法的时间性能时,主要标准就是算法的渐近时间复杂度,因此,在算法分析时,往往对两者不予区分,经常是将渐近时间复杂度T(n)=O(f(n))简称为时间复杂度,其中的f(n)一般是算法中频度最大的语句频度。此外,算法中语句的频度不仅与问题规模有关,还与输入实例中各元素的取值相关。但是我们总是考虑在最坏的情况下的时间复杂度。以保证算法的运行时间不会比它更长。 常见算法时间复杂度: O(1):表示算法的运行时间为常量 O(n):表示该算法是线性算法 O(㏒2n):二分查找算法 O(n2):对数组进行排序的各种简单算法,例如直接插入排序的算法。 O(n3):做两个n阶矩阵的乘法运算 O(2n):求具有n个元素集合的所有子集的算法 O(n!):求具有N个元素的全排列的算法 优<---------------------------<劣 O(1)

pascal考试应试技巧

LongLongLive ChairmanMao NOIP 应试 技巧手册 大部分内容转载自 OIBH

最重要的是细节 1.文件输入输出 2.Close before HALT 3.注释! 4.变量赋初值,全局变量也不例外。 5.DP初始值、边界情况 6.-0<>0 7.坐标~~~又是坐标~~~。横纵,行列, 8.人工inline。 9.记忆化没记忆等于TLE。 10.fillchar要做好 11.While/for之后的begin end 12.10^8有9位,不是8位。 13.Int64不能用Read,也不能直接赋给一个大于Longint的值。 14.非等差循环用while不用for. 15.凡是分母为变量的除法、Div、Mod都需要想一想是否要判0 16.删除屏幕输出! 17.不要总把logn忽略掉。 18.有时候要有意采用ln,exp变*为+ 19.Gcd,Mod,Div的使用都应该注意正负。 20.实数运算永远记住用Zero! 21.聚焦边界! 22.非明文禁止者,皆不无可能。 23.算数要准确,比如bignum的范围 24.文件名切记要使用小写!

关于算法与策略 1.看审题画在卷子上的线 2.超时的搜索/DP往往能比错误的贪心得到更多的分。 3.专用的方法胜于通用的方法。 4.好题往往不能直接经典算法 5.n,n^2n很小时差不多 6.如果一个数学问题很复杂,那么看结果找规律有可能比数学推导快。 7.即使是多项式算法,有时也可以加入很有效的剪枝。 8.矩阵的统计问题,降维策略,在外层用土方法,内层使用优化过的方法。 9.用O(N*N)枚举出Y1,Y2,然后考察之间夹的矩形是非常常用的方法。 10.涉及01串的问题,都不要忘记位运算和压缩,同时也要小心。 11.对于判重问题,关注最小表示。 12.对于想不出算法的题目,先有序化! 13.子序列之和的问题Sb-Sa 14.对于一边明显长于另外一边的矩形问题 15.没有回溯的搜索是最成功的搜索 16.最大规模的数据非常难设计,就不要管他 17.尽量让不做已做过的事和显然没有必要的事 18.不要解决无用的子问题和对结果进行无意义的引用 19.一般情况下,根据数据规模猜算法是非常有效的 20.Qsort算法要注意应该先存储选为基点的那个数以后再比较 21.关注最内层的循环到底在干什么 22.在只涉及乘除的高精度运算中,按因数存储 23.DFS之后森林不是树,重边? 24.优化的是针对大多数情况,不是特殊情况 25.即使是自己建的图中也有可能出现重边 26.“不超前”属性的引入可以使复杂度降低一个数量级。 27.DP的Downto应用 28.变量的取值范围与计算公式同等重要。 29.博弈问题从残局或结局出发 30.涉及单词的问题,对每个单词记录起止点,这样就充分利用了空间。 31.邻接矩阵+邻接链表 32.不如笨拙的枚举各种情况,只是注意在Copy&Paste的时候不要出错。 33.考虑坐标的定义是基于最小区间的还是基于点的。 34.双向搜索,就两边分别写过程 35.很多时候,输入的两个数据并没有说明两者的大小关系! 36.枚举的时候不要忘记想一想是用To还是Downto更好。 37.编写DFS之前一定要先考虑最坏的情况下栈空间是否够用。 38.A mod8=A and7 39.反复调用子程序。

常用算法

常用算法 一. 基本概念: 1. 算法:就是解决问题方法的精确描述。并不是所有问题都有算法,有些问题经研究可行,则相应有算法;而有些问题不能说明可行,则表示没有相应算法。 算法具有以下性质:是一有穷动作的序列; 动作序列仅有一个初始动作; 序列中每个动作的后继动作是确定的; 序列的终止表示问题得到解答或问题没有解答 2. 算法的分类:数值的和非数值的 数值的算法是以数学方式表示的问题求数值解的方法,如:代数方程 计算、矩阵计算、线性方程组求解、函数方程求解等; 非数值的算法是求非数值解的方法,如排序查找、模式匹配、排列模 拟、表格处理、文字处理等。 3. 算法设计:主要是针对各类具体问题设计良好的算法及研究设计算 法的规律和方法。 4. 常用的算法设计方法: 数值算法:迭代法、递归法、插值法等; 非数值算法:分治法、贪婪法、回溯法等。 5. 算法分析:是对设计出的每一个具体的算法,利用数学工具,讨论 各种复杂度。算法的复杂度分时间复杂度和空间复杂度。 二. 常用数值计算算法 1. 迭代法(i teration ) 迭代法适用于方程(或方程组)求解,是使用间接方法求方程近似根的一种常用算法。(参见清华版《PASCAL 程序设计P89练习4.23》 设方程f(x)=0,该方法将方程表示为等价形式:x=g(x),或一般地将f(x)拆成两个函数f 1、f 2,即f(x)= f 1(x)-f 2(x) =0,因而有f 1(x)=f 2(x)。其中f 1(x)是这样一个函数,对于任意数c ,容易求出f 1(x)=c 的精确度很高的实根。迭代法求解算法如下: (1). 首先选一个x 的近似根x 0,从x 0出发,代入右面函数,并解 方程f 1(x)=f 2(x 0)得到下一个近似根x 1; (2). 将上次近似根x 1代入右面函数,并解方程f 1(x)=f 2(x 1),得到 又一个近似根x 2 (3). 重复(2)的计算,得到一系列近似根x 0,x 1,x 2,…,x i ,x i+1,…,x n ,…; 若方程有根,这数列收敛于方程的根,若满足ε 1--n n x x ,则认为x n 是方程的近似根。

pascal语言基本知识

信息学奥赛讲义 前言关于信息学奥赛 一、什么是信息学奥赛: 信息学奥赛是形式:参赛学生在规定的3个小时内,完成4个与数学(涵盖小学奥数、中学数学、大学数学)有关的问题的计算机程序设计。阅卷采取计算机自动限时测试(黑箱测试法),通常限时为1秒,超时不得分。每道题测试10个(组)不同数据,通常是由简道难,每个测试点10分,共400分,根据得分多少确定得奖等次。 IOI:国际奥林匹克信息学竞赛 1989年在保加利亚的布拉维茨开始首届举行的一年一度的中学生竞赛,每个国家可以由4人组成国家队参加比赛,共有100多个国家参赛,至今已举办了21届。中国从第一届开始参赛。 作为五项国际奥林匹克学科竞赛之一,信息学奥林匹克竞赛是由联合国教科文组织于1988年发起创建、由来自世界各地20岁以下的中学生参加的计算机科学领域的一项赛事,目的是在青少年中普及计算机科学,为来自世界各地的年轻人提供一个交流机会,并通过比赛和访问学习主办国优秀的文化,加深对主办国的了解。竞赛每年在不同国家举办。中国累计获金牌30块、银牌17块,铜牌12块,安徽省累计获得金牌2块、银牌4块,铜牌5块. NOI:全国信息学奥林匹克竞赛 由中国计算机学会主办的一项面向全国青少年的信息学竞赛,也是与联合国教科文组织提倡的国际信息学奥林匹克竞赛同步进行的一项竞赛活动。1984年开始首届比赛,每个省选拔5名(2000年前4名)学生组成省队参加比赛,最终选拔出5名学生参加IOI竞赛。 安徽省从首届开始参加比赛,至今已9次获得团体第一,且各次均名列前5名。 AHOI:安徽省信息学奥林匹克竞赛 安徽省组队参加NOI的选拔赛,铜陵市从首届开始参赛,上实际90年代曾多次获得团体总分第一,至今仍保持前5名。 NOIP:全国信息学奥林匹克联赛 由中国计算机学会主办的一项面向全国青少年的普及性信息学竞赛,参加人数较多、设奖面较大。目前,NOIP分为普及组和提高组两个级别。 提高组:主要面向高中学生,是目前高中阶段五大联赛之一。设奖面大,2008年为例:安徽省设一等奖近50名。一等奖获得者将取得高考保送生资格。初中学生也可以报名参加。 普及组:主要面对初中学生,是安徽目前初中阶段唯一奥赛。按照铜陵市中考政策,获得普及组二等奖及以上者,中考获10分加分,同时可免试进入一中理科实验班。 铜陵市从2005年起参加该项比赛活动。已先后数十人次获得提高组一等奖,已毕业学生均已保送进入名牌大学(中国科大、复旦大学、上海交大等),今年高三学生目前已有8人获得NOIP一等奖取得保送生资格。 二、信息学奥赛学什么: 1、计算机语言: 由计算机指令组成的命令集。可控制计算机自动完成某一完整的工作。目前信息学竞赛可以使用的语言有Pascal、C、C++,本期将进行Pascal语言教学。 2、数据结构: 将数学对象和事物对象表达成计算机可以接受的形式,并根据特点把它们有机地组合在一起,勾连数据之间的关系,以便高速高效地加以处理。

相关主题
文本预览
相关文档 最新文档