6.4.4 欧拉回路

有一条名为Pregel的河流经过Konigsberg城,城中有7座桥,把河中的两个岛与河岸连接起来,当地居民热衷于一个难题,是否存在一条路线,可以不重复地走遍7座桥
首先是抽象为平常中我们常见的一笔画问题,这样的路线称为欧拉道路(eulerian path)

点击查看欧拉回路
C..................
.  .              .
.  .              .
A.................D
.  .              .
.  .              .
B..................

不难发现,在欧拉道路中,“进”和“出”是对应的--除了起点和终点外,其它点的“进出”次数应该相等,换句话说,除了起点和终点外,其他点的度数(degree)应该是偶数
很可惜在七桥问题中,所有四个点的度数均是奇数(这样的点也称为奇点),因此不可能存在欧拉道路,上述条件也是一个充分条件--如果一个无向图是联通的,且最多只有两个奇点,则一定存在欧拉道路。如果有两个奇点,则必须从其中一个奇点出发,另一个奇点终止;如果奇点不存在,则可以从任意点出发,最终一定会回到该点(即起点和终点重合,这个可以通过反证法来证明的),此时这样特殊的道路叫做欧拉回路
用类似推理方式可以得到有向图的结论:最多只能有两个点的入度不等于出度,而且必须是其中一个点的出度恰好比入度大1(把它作为起点),另一个的入度比出度大1(把它作为终点)
当然还有一个前提条件,在忽略边的方向后,图必须是联通的,否则与欧拉道路的题设不服(无向图老问题了,此时退化成有向图)

下面是程序,它同时适用于欧拉道路和欧拉回路(终点和起点是同一点,或者说不存在奇点),但是如果需要打印的是欧拉道路,在主程序中调用的时候,参数必须是道路的起点,另外,打印的顺序是逆序的,因此在真正使用这份代码时,应当把printf语句替换成一条push语句,把边(u,v)压入一个栈内

点击查看代码
void euler(int u) {
  for(int v = 0; v < n; v++) if(G[u][v] && !vis[u][v]) {
  	vis[u][v] = vis[v][u] = 1;//这边应该是标记该边是否已经走过 
  	euler(v);//进行递归 
  	printf("%d %d\n", u, v);
  }
}

尽管上面的代码只使用于无向图,但不难改成有向图,把vis[u][v]=vis[v][u]=1改成vis[u][v]=1就可以了,从无向退化成有向了
根据连通性和度数可以判断出无向图和有向图是否存在欧拉道路和欧拉回路
可以用DFS构造欧拉回路(这边的思路参考上节中的拓扑排序中的dfs,两者的构造有非常类似的地方,只不过欧拉回路允许存在有向环罢了)
注意做图的题目的时候时常需要有抽象思维进行图像化

Play_On_Words题解
切记open的过去式没有双写,不要习惯性多打字母opened

上一篇:zoj4062


下一篇:Linux常用命令