#define infinity 1000000 // a big int
#define max_vertexes 50 // the max count of vertexes
typedef int Graph[max_vertexes][max_vertexes]; // use adjacent matrix to represent graph
/*********************************************
求最小生成树的Prim算法
用邻接矩阵表示图,复杂度为O(n^2)
参数G表示图的邻接矩阵,
vcount表示图的顶点个数,
father用来记录每个节点的父节点
**********************************************/
void prim(Graph G,int vcount,int father[])
{
int i,j,k;
int lowcost[max_vertexes], closeset[max_vertexes], used[max_vertexes];
for( i = 0; i < vcount; i++ )
{
lowcost[i] = G[0][i];
closeset[i] = 0; // notice: here vertex 1 is G[0]
used[i] = 0; // mark all vertexes have not been used
father[i] = -1; // that means no father
}
used[0] = 1; // mark vertex 1 has been used
for( i=1; i< vcount; i++)
{
j=0;
while( used[j] ) j++;
for(k=0; k<vcount; k++)
if ( ( !used[k] ) && ( lowcost[k] < lowcost[j] ) )
{
j=k; // find the next tree edge
}
father[j]=closeset[j]; // record the tree edge using father array
used[j]=1; // mark vertex j+1 has been used
for (k=0;k<vcount;k++)
if (!used[k]&&(G[j][k]<lowcost[k])) // modify the lowcost
{
lowcost[k]=G[j][k];
closeset[k]=j;
}
}
}
================================================*/
int Dijkstra(Graph G,int n,int s,int t, int path[])
{
int i,j,w,minc, d[max_vertexes], mark[max_vertexes];
for (i=0; i<n; i++) mark[i]=0;
function Dijkstra(var G:adj_matrix;n:integer;s,t:integer;var Path:array of integer):integer;
var
d:array[1..MaxSize]of integer; {用来纪录从源点到所有点的最短路径的长度}
mark:array[1..MaxSize]of 0..1; {用来标记已经找到最短路径的顶点,0表示已经找到最短路径,1表示还未找到最短路径}
i,j,w,min:integer;
begin
FillChar(mark,SizeOf(mark),0);
mark[s]:=1;
for i:=1 to n do {这里规定s,i不相邻时G[i,j]=∞}
begin
d[i]:=G[s,i];
Path[i]:=s;
end;
Path[s]:=0;
d[s]:=0;
for i:=1 to n-1 do
begin
min:=maxint;
w:=0;
for j:=1 to n do {找到所有还没有找出最短路径的节点中到s最近的节点w}
if (mark[j]=0)and(min>=d[j]) then
begin
min:=d[j];
w:=j;
end;
mark[w]:=1;
for j:=1 to n do {修改所有还没有找出最短路径的节点到s的距离}
if (mark[j]=0)and(G[w,j]<>maxint)and(d[j]>d[w]+G[w,j]) then
begin
d[j]:=d[w]+G[w,j];
Path[j]:=w;
end;
end;
result:=d[t];
end;
{==================================
单源最短路径
Bellman-Ford 算法
适用条件:所有情况,边权可以为负
===================================}
function Bellman_Ford(var G:adj_matrix;size:integer;s,t:integer;var path:array of integer;var success:boolean):integer;
var
d:array of integer; {用来纪录从源点到所有点的最短路径的长度}
i,j,k:integer;
begin
setlength(d,size+1);
for i:=1 to size do
begin
d[i]:=maxint div 2; {注意:不可以将无穷大取maxint,否则会溢出;取maxint div 2就可以了}
path[i]:=0;
end;
d[s]:=0;
for k:=1 to size-1 do
for i:=1 to size do
for j:=1 to size do
if (d[j]>d[i]+G[i,j]) then {注意,这里i若和j不相邻,则必须规定G[i,j]=∞,但是不可以将无穷大取maxint,否则会溢出;取maxint div 2就可以了}
begin
d[j]:=d[i]+G[i,j];
path[j]:=i;
end;
for i:=1 to size do
for j:=1 to size do
if (d[j]>d[i]+G[i,j]) then
begin {存在一个从源点可达的权为负的回路}
success:=false;
result:=0;
exit;
end;
result:=d[t];
success:=true;
end;
{==========================================
最小生成树
Prim 算法
基本思想:
T为最小生成树的边集;
U为一个顶点集
V为图的顶点集
图的顶点标号为1..n
procedure Prim;
begin
T:=Φ;
U:={1};
while U<>V do
begin
(u,v):= u∈U且v∈V-U的最小权边;
T:=T∪{(u,v)};
U:=U∪{v};
end;
end;
======================}
procedure Prim(var G:adj_matrix;size:integer);{G为图,size为图的节点数目;注意:假设输入的图是连通图}
var
lowcost:array [1..maxsize] of integer;
used:array [1..maxsize] of boolean;
closeset:array[1..maxsize] of integer;
i,j,k,min:integer;
begin
for i:=2 to size do {初始化,此时U只含有顶点1}
begin
lowcost[i]:= G[1,i];
closeset[i]:=1;
used[i]:=false;
end;
used[1]:=true;
for i:=2 to size do
begin
min:=maxint;
j:=i;
for k:=2 to size do {用选择法寻找顶点分别在V-U与U中权最小的边}
if (not used[k])and(lowcost[k]<min) then
begin
min:=lowcost[k];
j:=k;
end;
writeln(fout,'(',closeset[j],',',j,')'); {输出找到的最小生成树的一条边,此处可根据情况修改}
used[j]:=true; {将j填加到U}
for k:=2 to size do {调整lowcost和closeset}
if (not used[k])and(G[j,k]<lowcost[k]) then
begin
lowcost[k]:=G[j,k];
closeset[k]:=j;
end;
end;
end;
{==========================================
每对节点间最短路径
Flod-Warshall 算法
===========================================}
procedure Flod_Warshall(var G:adj_matrix;size:integer;var D,P:adj_matrix);
{D[i,j]表示从i到j的最短距离;P[i,j]表示从i到j的最短路径上j 的父节点}
var
i,j,k:integer;
begin
{初始化}
for i:=1 to size do {这里假设i,j不相邻时G[i,j]=∞。注意:为了防止溢出,无穷大不能取maxint,可以取maxint div 2}
for j:=1 to size do
begin
D[i,j]:=G[i,j];
P[i,j]:=i; {P[i,j]=0表示不存在从i到j的路径}
end;
for i:=1 to size do
begin
D[i,i]:=0;
P[i,i]:=0;
end;
for k:=1 to size do {进行size次迭代}
for i:=1 to size do
for j:=1 to size do
if (d[i,j]>d[i,k]+d[k,j]) then
begin
d[i,j]:=d[i,k]+d[k,j];
p[i,j]:=p[k,j];
end;
end;
{==========================================
求有向图的传递闭包
G[i,j]=1表示i,j相邻;G[i,j]=0表示i,j不相邻
===========================================}
procedure Transtive_Closure(var G:adj_matrix;size:integer;var T:TranstiveClosureType);
var
i,j,k:integer;
begin
for i:=1 to size do
for j:=1 to size do
if G[i,j]<>0 then //若i与j不相邻则G[i,j]=0,此处可根据情况修改
T[i,j]:=true;
for i:=1 to size do T[i,i]:=true;
for k:=1 to size do
for i:=1 to size do
for j:=1 to size do
T[i,j]:=T[i,j] or (T[i,k] and T[k,j]);
end;
============================================}
procedure DFS(var G:adj_matrix;size:integer);
const
white=0;
gray=-1;
black=1;
var
EdgeType:adj_matrix;
d,f,c,p,b,l:array[1..MaxSize] of integer;
u,time:integer;
procedure DFS_visit(u:integer);
var
v:integer;
begin
c[u]:=gray;
inc(time);
d[u]:=time;
l[u]:=time;
for v:=1 to size do
if G[u,v]>0 then {G[u,v]>0表示边(u,v)存在且未被访问,当边(u,v)不存在时G[u,v]=0}
case c[v] of
white: begin
{!!!} {G[u,v]:=-G[u,v]; {设置(u,v)暂时不可访问的标志,对于有向图,请把此行删去}
EdgeType[u,v]:=1; {(u,v)为树枝边}
inc(b[u]); {深度优先树中顶点u的度数加1}
p[v]:=u; {深度优先树中顶点v的父亲是u}
DFS_visit(v); {深度优先搜索顶点v}
if l[v]<l[u] then l[u]:=l[v]; {u的儿子v的关联边全部被访问时,l[u]=min(l[u],l[v])}
{!!!} {G[u,v]:=-G[u,v]; {恢复(u,v)可访问,对于有向图,请把此行删去}
end;
gray: begin
EdgeType[u,v]:=2; {(u,v)为反向边}
if d[v]<l[u] then l[u]:=d[v]; {访问反向边(u,v)时,l[u]:=min(l[u],d[v])}
end;
black: begin
if d[v]>d[u] then EdgeType[u,v]:=3 {(u,v)为正向边}
else EdgeType[u,v]:=4; {(u,v)为交叉边}
end;
end; {end of case}
c[u]:=black;
inc(time);
f[u]:=time;
end;
{===============================================
最大流算法
基于Ford-Fulkerson 算法的 Edmonds-Karp 实现
求出以s为源,以t为汇的最大网络流
时间复杂度为O(VE^2)
C为容量网络,如果(u,v)没有边,规定c[u,v]=0;
注意,Flow是一个已经求出的可行流,一般可以取Flow[i,j]=0;
================================================}
procedure Edmonds_Karp(var C:adj_matrix;size:integer;s,t:integer;var Flow:adj_matrix);
var
L:array [1..maxsize,1..2] of integer; {记录标号}
Q:array [1..maxsize] of integer; {队列}
u,v,head,tail:integer;
begin
repeat
fillchar(L,sizeof(L),0); {初始化,所有点标号为0}
L[s,1]:=0;
L[s,2]:=maxint;
head:=0;
tail:=1;
Q[tail]:=s; {s入队}
while (head<tail)and(L[t,2]=0) do {当Q非空并且汇点未标号}
begin
inc(head);
u:=Q[head];
for v:=1 to size do
if (Flow[u,v]<C[u,v])and(L[v,2]=0) then {如果v没有标号且(u,v)的流量可改进}
begin
inc(tail);
Q[tail]:=v;
L[v,2]:=min(C[u,v]-Flow[u,v],L[u,2]); {L[v,2]记录下从s到v的增广路径中最小的可改进流量}
L[v,1]:=u; {L[v,1]记录下从s到v的增广路径中v的前驱}
end;
end; {while}
if L[t,2]>0 then {如果汇点已标号}
begin
v:=t;
u:=L[v,1];
while v<>s do
begin
Flow[u,v]:=Flow[u,v]+L[t,2];
Flow[v,u]:=-Flow[u,v];
v:=u;
u:=L[v,1];
end; {while}
end; {then}
until L[t,2]=0; {直到汇点未标号}
end;
function Max_Flow_With_Limt(var B,C:adj_matrix;size:integer;s,t:integer;var Flow:adj_matrix):boolean;
var
new_c:adj_matrix; {新的容量网络}
i,j:integer;
begin
fillchar(new_c,sizeof(new_c),0);
{构造一个新的容量网络new_c}
for i:=1 to size do {增加一个新汇点size+2}
for j:=1 to size do
new_c[i,size+2]:=new_c[i,size+2]+B[i,j];
for i:=1 to size do {增加一个新源点size+1}
for j:=1 to size do
new_c[size+1,i]:=new_c[size+1,i]+B[j,i];
for i:=1 to size do
for j:=1 to size do
new_c[i,j]:=C[i,j]-B[i,j];
new_c[s,t]:=maxint;
new_c[t,s]:=maxint;
{用Edmonds_Karp算法在新的容量网络中求最大流}
fillchar(flow,sizeof(flow),0);
Edmonds_Karp(new_c,size+2,size+1,size+2,flow);
{检验原容量限制网络是否有可行流}
for i:=1 to size do
if flow[size+1,i]<new_c[size+1,i] then
begin
result:=false;
exit;
end;
result:=true;
{将新容量网络中求出的可行流化为原来的容量网络中的可行流}
flow[s,t]:=C[s,t];
flow[t,s]:=-flow[s,t];
for i:=1 to size do
for j:=1 to size do
if flow[i,j]>=0 then
begin
flow[i,j]:=flow[i,j]+B[i,j];
flow[j,i]:=-flow[i,j];
end;
{用Edmonds_Karp将求出的可行流进行放大}
Edmonds_Karp(c,size,s,t,flow);
end;
cost ← 0;
repeat
可改进路撤空;
设源点的CT值为0并进入可改进路;
repeat
break ← false;
for u ←1 to N do
begin
分析U出发的所有弧<U,V>;
if (<U,V>的流量可改进)and(源点至U有通路)and(U的CT值+<U,V>的费用 < V的CT值) then
begin
break ← true;
V的CT值 ← U的CT值+<U,V>的费用;
V进入可改进路经并为之标号;
end if
end for
until break=false
if 汇点已标号 then
begin
从汇点出发倒向修正可改进路的流量;
cost ← cost + ∑B(e)*d(其中e∈P,d为P的可改进量);
end if
until 汇点未标号;
repeat
path置空;
源点s标号并进入path和Q;
while Q非空 and 汇点t未标号 do
begin
移出Q的队首顶点u;
for 每一条从u出发的弧(u,v) do
if v未标号 and 弧(u,v)的流量可改进
then v进入队列Q和path;
end while
if 汇点已标号
then 从汇点出发沿着path修正可改进路的流量;
until 汇点未标号;