本文目录一览:
- 1、java 最短路径算法 如何实现有向 任意两点的最短路径
- 2、有什么无权无向图的最短路径算法比较好,求一个用java实现的
- 3、最小费用流和最小费用最大流有什么区别?
- 4、网络流的最小费用流算法
- 5、求最短路径算法
java 最短路径算法 如何实现有向 任意两点的最短路径
Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表方式
用OPEN,CLOSE表的方式,其采用的是贪心法的算法策略,大概过程如下:
1.声明两个集合,open和close,open用于存储未遍历的节点,close用来存储已遍历的节点
2.初始阶段,将初始节点放入close,其他所有节点放入open
3.以初始节点为中心向外一层层遍历,获取离指定节点最近的子节点放入close并从新计算路径,直至close包含所有子节点
代码实例如下:
Node对象用于封装节点信息,包括名字和子节点
[java] view plain copy
public class Node {
private String name;
private MapNode,Integer child=new HashMapNode,Integer();
public Node(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MapNode, Integer getChild() {
return child;
}
public void setChild(MapNode, Integer child) {
this.child = child;
}
}
MapBuilder用于初始化数据源,返回图的起始节点
[java] view plain copy
public class MapBuilder {
public Node build(SetNode open, SetNode close){
Node nodeA=new Node("A");
Node nodeB=new Node("B");
Node nodeC=new Node("C");
Node nodeD=new Node("D");
Node nodeE=new Node("E");
Node nodeF=new Node("F");
Node nodeG=new Node("G");
Node nodeH=new Node("H");
nodeA.getChild().put(nodeB, 1);
nodeA.getChild().put(nodeC, 1);
nodeA.getChild().put(nodeD, 4);
nodeA.getChild().put(nodeG, 5);
nodeA.getChild().put(nodeF, 2);
nodeB.getChild().put(nodeA, 1);
nodeB.getChild().put(nodeF, 2);
nodeB.getChild().put(nodeH, 4);
nodeC.getChild().put(nodeA, 1);
nodeC.getChild().put(nodeG, 3);
nodeD.getChild().put(nodeA, 4);
nodeD.getChild().put(nodeE, 1);
nodeE.getChild().put(nodeD, 1);
nodeE.getChild().put(nodeF, 1);
nodeF.getChild().put(nodeE, 1);
nodeF.getChild().put(nodeB, 2);
nodeF.getChild().put(nodeA, 2);
nodeG.getChild().put(nodeC, 3);
nodeG.getChild().put(nodeA, 5);
nodeG.getChild().put(nodeH, 1);
nodeH.getChild().put(nodeB, 4);
nodeH.getChild().put(nodeG, 1);
open.add(nodeB);
open.add(nodeC);
open.add(nodeD);
open.add(nodeE);
open.add(nodeF);
open.add(nodeG);
open.add(nodeH);
close.add(nodeA);
return nodeA;
}
}
图的结构如下图所示:
Dijkstra对象用于计算起始节点到所有其他节点的最短路径
[java] view plain copy
public class Dijkstra {
SetNode open=new HashSetNode();
SetNode close=new HashSetNode();
MapString,Integer path=new HashMapString,Integer();//封装路径距离
MapString,String pathInfo=new HashMapString,String();//封装路径信息
public Node init(){
//初始路径,因没有A-E这条路径,所以path(E)设置为Integer.MAX_VALUE
path.put("B", 1);
pathInfo.put("B", "A-B");
path.put("C", 1);
pathInfo.put("C", "A-C");
path.put("D", 4);
pathInfo.put("D", "A-D");
path.put("E", Integer.MAX_VALUE);
pathInfo.put("E", "A");
path.put("F", 2);
pathInfo.put("F", "A-F");
path.put("G", 5);
pathInfo.put("G", "A-G");
path.put("H", Integer.MAX_VALUE);
pathInfo.put("H", "A");
//将初始节点放入close,其他节点放入open
Node start=new MapBuilder().build(open,close);
return start;
}
public void computePath(Node start){
Node nearest=getShortestPath(start);//取距离start节点最近的子节点,放入close
if(nearest==null){
return;
}
close.add(nearest);
open.remove(nearest);
MapNode,Integer childs=nearest.getChild();
for(Node child:childs.keySet()){
if(open.contains(child)){//如果子节点在open中
Integer newCompute=path.get(nearest.getName())+childs.get(child);
if(path.get(child.getName())newCompute){//之前设置的距离大于新计算出来的距离
path.put(child.getName(), newCompute);
pathInfo.put(child.getName(), pathInfo.get(nearest.getName())+"-"+child.getName());
}
}
}
computePath(start);//重复执行自己,确保所有子节点被遍历
computePath(nearest);//向外一层层递归,直至所有顶点被遍历
}
public void printPathInfo(){
SetMap.EntryString, String pathInfos=pathInfo.entrySet();
for(Map.EntryString, String pathInfo:pathInfos){
System.out.println(pathInfo.getKey()+":"+pathInfo.getValue());
}
}
/**
* 获取与node最近的子节点
*/
private Node getShortestPath(Node node){
Node res=null;
int minDis=Integer.MAX_VALUE;
MapNode,Integer childs=node.getChild();
for(Node child:childs.keySet()){
if(open.contains(child)){
int distance=childs.get(child);
if(distanceminDis){
minDis=distance;
res=child;
}
}
}
return res;
}
}
Main用于测试Dijkstra对象
[java] view plain copy
public class Main {
public static void main(String[] args) {
Dijkstra test=new Dijkstra();
Node start=test.init();
test.computePath(start);
test.printPathInfo();
}
}
有什么无权无向图的最短路径算法比较好,求一个用java实现的
有什么无权无向图的最短路径算法比较好
带权图也分有向和无向两种,基本的算法可以看看书咯。 带权的无向图的最短路径又叫最小生成树,Prim算法和Kruskal算法; 带权的有向图的最短路径算法有迪杰斯特拉算法和佛洛依德算法;
String[] s={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
System.out.print("请输入数字(1-12):");
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String str=br.readLine();
int m=Integer.parseInt(str);
if (m=0||m=13)
{
最小费用流和最小费用最大流有什么区别?
最小费用最大流是指:满足最大流的情况下,让费用最小。
最小费用流:仅要求费用最小,通常情况下有费用为负的边权(如果费用全为正,那么可以让流量为0,费用也就是0),可以使用最小费用最大流的算法求解,只不过终止条件变为“从原点到汇点的费用为正”
最小费用最大流算法的原本终止条件为“从原点到汇点的容量为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).
三,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),现在给每条弧Vi, Vj加上一个下界限制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,称vi, vj为饱和弧;否则称vi, vj为非饱和弧。若fij = 0,称vi, vj为零流弧;否则称vi, vj为非零流弧。
定义一条道路P,起点是S、终点是T。把P上所有与P方向一致的弧定义为正向弧,正向弧的全体记为P+;把P上所有与P方向相悖的弧定义为反向弧,反向弧的全体记为P-。
譬如在图5-1中,P = (S, V1, V2, V3, V4, T),那么
P+ = {S, V1, V1, V2, V2, V3, V4, T}
P- = {V4, V3}
给定一个可行流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) = S, V2 + V1, V2 + V1, V3+V1, V4=8+4+4+1=17
定理:对于已知的网络流图,设任意一可行流为f,任意一割切为(U, W),必有:V(f) ≤ C(U, W)。
通俗简明的讲:“最大流小于等于任意割”。这是“流理论”里最基础最重要的定理。整个“流”的理论系统都是在这个定理上建立起来的,必须特别重视。
下面我们给出证明。
网络流、可改进路、割切都是基础的概念,应该扎实掌握。它们三者之间乍一看似乎风马牛不相干,其实内在联系是十分紧密的。
二、求最大流
何谓最大流?首先它必须是一个可行流;其次,它的流量必须达到最大。这样的流就称为最大流。譬如对图5-1而言,它的最大流如下:
下面探讨如何求得最大流。
在定义“可改进路”概念时,提到可以通过一定规则修改“可改进路”上弧的流量,可以使得总流量放大。下面我们就具体看一看是什么“规则”。
对可改进路P上的弧vi, vj,分为两种情况讨论:
第一种情况:vi, vj∈P+,可以令fij增加一个常数delta。必须满足fij + delta ≤ cij,即delta ≤ cij – fij。
第二种情况:vi, vj∈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,且fxycxy,则y∈U;
若x∈U,且fyx0,则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),每条弧Vi, Vj对应两个非负整数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、 若Vi, Vj∈E,fijCij,那么Vi, Vj∈E’,权为Wij。
若Vi, Vj∈E,fij0,那么Vj, Vi∈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)
step 1. 令Short[k] +∞(1≤k≤n+1),Short[0] 0。
step 2. 遍历每一条弧Vk, Vj。若Short[k] + k, j Short[j],则令Short[j] Short[k] + k, 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的最小费用最大流。你能解决吗?
4.C++代码实现 #includecstdio#includecstring#includealgorithm#includequeueusingnamespacestd;intH[105],X[40005],P[40005],from[40005],flow[40005],cost[40005],tot;inlinevoidadd(intx,inty,intz,intk){P[++tot]=y;X[tot]=H[x];H[x]=tot;flow[tot]=z;from[tot]=x;cost[tot]=k;}intf,c;intn,m;intS,T;intd[105],a[105],p[105];structN{intx,w;friendbooloperator(Na,Nb){returna.wb.w;}N(inta=0,intb=0){x=a,w=b;}};priority_queueNq;boolspfa(){memset(d,0x3f,sizeof(d));d[S]=0;a[S]=inf;p[S]=0;intx;q.push(N(S,0));while(!q.empty()){x=q.top().x;q.pop();if(x==T)continue;for(inti=H[x];i;i=X[i]){if(flow[i]0d[P[i]]d[x]+cost[i]){d[P[i]]=d[x]+cost[i];a[P[i]]=min(a[x],flow[i]);p[P[i]]=i;q.push(N(P[i],d[P[i]]));}}while(!q.empty()d[q.top().x]!=q.top().w)q.pop();}if(d[T]==inf)return0;f+=a[T];c+=a[T]*d[T];x=T;while(x!=S){flow[p[x]]-=a[T];flow[p[x]^1]+=a[T];x=from[p[x]];}return1;}intmain(){#ifdefLOCALfreopen(in.txt,r,stdin);freopen(out.txt,w,stdout);#endifInput();while(spfa());maxflow=f,mincost=c;return0;}四、有上下界的最大流
上面讨论的网络流都只对每条弧都限定了上界(其实其下界可以看成0),现在给每条弧Vi, Vj加上一个下界限制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,就添加一条弧S’, x,其上界为h-(x)。
3、 对每个顶点x,令 ,若h+(x)≠0,就添加一条弧x, T’,其上界为h+(x)。
4、 对于任何Vi, Vj∈E,都有Vi, Vj∈E’,其上界C’ij = Cij – Aij。
5、 新增T, S∈E’,其上界CTS = +∞。
在G’中以S’为源点、T’为汇点求得最大流f’。若f’中从S’发出的任意一条弧是非饱和弧,则原网络流图没有可行流。否则可得原图的一个可行流f = f’ + A,即所有的fij = f’ij + Aij。(其正确性很容易证明,留给读者完成)
然后再求可改进路(反向弧Vi, Vj必须满足fij≥Aij,而非fij≥0),不断放大f,直到求出最大流。
我们看到,上几节所讨论的一种可行网络流实际上是{Aij = 0}的一种特殊网络流,这里提出的模型更一般化了。解决一般化的复杂问题,我们采取的思路是将其转化为特殊的简单问题,加以研究、推广,进而解决。这是一种重要的基本思想:化归——简单有效。基于这种思想,请读者自行思考解决:
1、 有上下界的最小流。
2、 有上下界的最小费用最大流。
五、多源点、多汇点的最大流
已知网络流图有n个源点S1、S2、……、Sn,m个汇点T1、T2、……、Tm,,求该图的最大流。这样的问题称为多源点、多汇点最大流。
它的解决很简单:
1、 增设一个“超级源”S’,对每个源点Si,新增弧S’, Si,容量为无穷大。
2、 增设一个“超级汇”T’,对每个汇点Ti,新增弧Ti, T’,容量为无穷大。
3、 以S’为源点、T’为汇点求最大流f。
4、 将f中的S’和T’去掉,即为原图的最大流。
算法正确性显然。
六、顶点有容量限制的最大流
上一节已经提出了这个问题,即对于进出每个顶点的流量也规定一个上限,这样的最大流如何求?
既然我们已经解决了“边限制”问题,现在何不把“点限制”问题转化为“边限制”呢?具体办法如下:
1、 对除源点和汇点之外的每个顶点i拆分成两个顶点i’和i’’。新增一条弧i’, i’’,其容量为点i的流量限制。
2、 对于原图中的弧i, j,我们将其变换成i’’, j’。
3、 对变换后的图求最大流即可。
这里我们又一次运用到了化归的思想:将未知的“点限制”问题转化为已知的“边限制”问题。
七、网络流与二部图的匹配
{二部图和匹配的定义可参见本书专门介绍二部图匹配的章节}
设二部图为G = (X, Y, E)。
增设点S’,对于所有i∈X,新增弧S’, Xi,容量为1;增设点T’,对于所有i∈Y,新增一条弧Yi, T’,容量也为1。原图中所有的弧予以保留,容量均为+∞。对新构造出来的网络流图以S’为源点、T’为汇点求最大流:流量即为最大匹配数;若弧Xi, Yj(i∈X,j∈Y)的流量非零,它就是一条匹配边。
二部图最大匹配问题解决。
那么二部图的最佳匹配问题又如何?
仍然按照上述方法构图。同时令原图中弧的费用保持不变;新增弧的费用置为0。然后以S’为源点、T’为汇点求最小费用最大流即可。最大流的费用即为原二部图最佳匹配的费用。
网络应用
将一连串的水管绘画成一个网络。每条水管有一特定的阔度,因此只可以保持一特定的水流量。当任何水管汇合,流入汇流点的总水量必须等于流出的水量,否则我们会很快地缺水,或者我们会团积水。我们有一个作为源点的入水口,和一个作为汇点的出水口。一道流便是一条由源点到汇点而使从出水口流出的总水量一致的可能路径。直观地,一个网络的总流量是水从出口流出的速率。
流可以关于在交通网络上的人或物质,或电力分配系统上的电力。对于任何这样的实物网络,进入任何中途结点的流需要等于离开那结点的流。Bollobás以基尔霍夫电流定律描绘这限制的特性,同时较迟的作者(即 Chartrand)提及它在某些守恒方程的普遍化。
在生态学中也可找到流网络的应用:当考虑在食物网中不同组织之间养料及能量的流,流网络便自然地产生。与这些网络有联繋的数学问题和那些液体流或交通流网络中所产生的难题有很大分别。由 Robert Ulanowicz 及其他人发展的生态系统网络分析领域包含使用信息论及热力学的概念去研究这些网络随时间的演变。
求最短路径算法
import java.awt.*;
import java.util.HashSet;
import java.util.Random;
class example2 {
private static Point[] mTestPoints;
//已知平面上N点坐标,求遍历所有点的最短路径.
public static void main(String[] args) {
//两点之间的距离 d=√(a^2+b^2) 其中a=|x1 –x2|;b=|y1 - y2|
//都是简单的正相关函数,距离最短那么需要a+b最小
//n个点需要求C(n,2)次
//其实java提供了两点之间距离的Api咱们直接使用即可
generateTestPoints();
double minDistance = Double.MAX_VALUE;
for (int i = 0; i mTestPoints.length; i++) {
//两两计算,数组中每个点只跟后面的点求距离
for (int j = i + 1; j mTestPoints.length; j++) {
double distance = mTestPoints[i].distance(mTestPoints[j]);
if (distance minDistance) {
minDistance = distance;
}
}
}
//得到结果
System.out.println("最短距离为:" + minDistance);
}
private static void generateTestPoints() {
//随机生成10个点的集合,为了去重使用hashSet
Random random = new Random();
HashSetPoint mPointSet = new HashSet();
for (int i = 0; i 10; i++) {
boolean add = mPointSet.add(new Point(random.nextInt(100), random.nextInt(100)));
if (!add) {
--i;
}
}
mTestPoints = mPointSet.toArray(new Point[10]);
}
}