图论

图 论

使用邻接表和邻接矩阵中的邻接矩阵描述

图的遍历

图例如下, 0为起点.    0   /
1   2   \ / 
   3   4

深度优先搜索(DFS)

N = 5

G = [
     [ 0, 1, 1, 0, 0 ],
     [ 1, 0, 0, 1, 0 ],
     [ 1, 0, 0, 1, 1 ],
     [ 0, 1, 1, 0, 0 ],
     [ 0, 0, 1, 0, 0 ]
]

V = [False for i in range(N)]

def dfs(n):
    print(n)
    for j in range(N):
        if not V[j] and G[n][j]:
            V[j] = True
            dfs(j)

V[0] = True
dfs(0)

广度优先搜索(BFS)

from queue import Queue

N = 5

G = [
     [ 0, 1, 1, 0, 0 ],
     [ 1, 0, 0, 1, 0 ],
     [ 1, 0, 0, 1, 1 ],
     [ 0, 1, 1, 0, 0 ],
     [ 0, 0, 1, 0, 0 ]
]

V = [False for i in range(N)]

Q = Queue()

def bfs(n):
    Q.put(n)
    V[0] = True
    
    while not Q.empty():
        i = Q.get()
        print(i)
        for j in range(N):
            if not V[j] and G[i][j]:
                Q.put(j)
                V[j] = True


bfs(0)

最小生成树

问题: 将一个图的所有顶点构成树, 要求边的权值和最小. 普里姆算法:从起点开始选择边, 每次添加一个顶点(边权值和最小)并调整已添加好的顶点集与其他顶点的最短距离(边权值最小), 递归此步骤. 克鲁斯卡尔算法:每次选择最短边, 如果构成环, 则放弃这条边, 递归此步骤.

普里姆(prim)算法

X = 100

N = 6

G = [
	[ X, 6, 1, 5, X, X ],
	[ 6, X, 5, X, 3, X ],
	[ 1, 5, X, 1, 5, 4 ],
	[ 5, X, 1, X, X, 2 ],
	[ X, 3, 5, X, X, 6 ],
	[ X, X, 4, 2, 6, X ]
]

class Edge:
	def __init__(self, begin, end, lens):
		self.begin, self.end = begin, end
		self.lens = lens
	def __len__(self):
		return self.lens
		

# init edge info
E = []

for i in range(1, N):
	E.append(Edge(0, i, G[0][i]))


# set kth edge
for k in range(0, N-1):

	# find shortest edge
	minz, idx = X, -1
	for i in range(k, N-1):
		if len(E[i]) < minz:
			minz = len(E[i])
			idx = i

	# swap edge
	E[k], E[idx] = E[idx], E[k]
	v = E[k].end

	# adjust edge
	for i in range(k+1, N-1):
		d = G[v][E[i].end]
		if d < len(E[i]):
			E[i].lens = d
			E[i].begin = v


for item in E:
	print(item.begin, item.end, len(item))

克鲁斯卡尔(kruskal)算法

查找是否在相同集合来避免产生环, 集合为存放后继的顶点链.

X = 100

N = 6

G = [
	[ X, 6, 1, 5, X, X ],
	[ 6, X, 5, X, 3, X ],
	[ 1, 5, X, 1, 5, 4 ],
	[ 5, X, 1, X, X, 2 ],
	[ X, 3, 5, X, X, 6 ],
	[ X, X, 4, 2, 6, X ]
]

class Edge:
	def __init__(self, begin, end, lens):
		self.begin, self.end = begin, end
		self.lens = lens
	def __len__(self):
		return self.lens
		

# init edge info
E = []

for i in range(N):
    for j in range(i+1, N):
        if G[i][j] != X:
            E.append(Edge(i, j, G[i][j]))

E.sort(key=lambda x: x.lens)


P = [0 for i in range(N)]

# save next vertex
def find_set(f):
    while P[f] > 0:
        f = P[f]
    return f

# set kth edge
cnt_edge = 0
for i in range(len(E)):
    
    # find set
    n = find_set(E[i].begin)
    m = find_set(E[i].end)

    # if no circle
    if n != m:
        # add next vertex for vertex n
        P[n] = m
        print(E[i].begin, E[i].end, len(E[i]))

        # N-1 edges finish
        cnt_edge += 1
        if cnt_edge == N-1:
            break

最短路径

问题: 计算一个顶点到其他所有顶点的最短路径. 迪杰斯特拉算法: 设置一个起点, 先确定一个最短路径顶点v(第一次确定是相邻的顶点), 再将v作为中间点, 调整起点与各顶点的距离, 递归此步骤.

迪杰斯特拉(dijkstra)算法

INF = 400

N = 5

G = [
	[   0,  10, INF,  30, 100 ],
	[ INF,   0,  50, INF, INF ],
	[ INF, INF,   0, INF,  10 ],
	[ INF, INF,  20,   0,  60 ],
	[ INF, INF, INF, INF,   0 ]
]


# init S & D & P
# S(Set for Vertex)
# D(Distance for vertex 0 to others)
# P(Prev vertex for vertex)

S, D, P = [], [], []
S.append(0)

for i in range(N):
    D.append(G[0][i])
    P.append(0)


# set kth vertex
for k in range(1, N):

    # select shortest edge
    minz = INF
    for i in range(N):
        if not i in S:
            if D[i] < minz:
                minz = D[i]
                v = i
    
    # add new vertex to S
    S.append(v)

    # adjust D(distance)
    for i in range(N):
        if not i in S:
            if D[i] > D[v]+G[v][i]:
                D[i] = D[v]+G[v][i]
                P[i] = v


# show shortest distance
print(D)

# show shortest path
for i in range(1, N):
    prev = P[i]
    print(i, end='')
    while prev != 0:
        print('<--%d' % prev, end='')
        prev = P[prev]
    print('<--%d' % 0)

网络流

二分图匹配问题 问题: 求二分图的最大边匹配(最小点覆盖). 匈牙利算法: 依次匹配, 遇到点已经被匹配的情况下, 尝试能否让被匹配的点的对点匹配其他点(寻找增广路径), 递归此步骤. 二分图待配对情况: 0 <=> 0, 1 1 <=> 1, 2 2 <=> 0 3 <=> 2

匈牙利(Hungary)算法

N = 4

G = [
    [ 1, 0, 1, 0 ],
    [ 1, 1, 1, 0 ],
    [ 0, 1, 0, 1 ],
    [ 0, 0, 0, 0 ]
]

R = [-1 for i in range(N)]

def find_path(n):
    for j in range(N):
        if not V[j] and G[n][j]:
            V[j] = True
            if R[j]==(-1) or find_path(R[j]):
                R[j] = n
                return True
    return False


cnt = 0

for i in range(N):
    V = [False for i in range(N)]
    if find_path(i):
        cnt += 1

print(cnt)
for i in range(N):
    if R[i] != (-1):
        print('%d <==> %d' % (i, R[i]))

最大流问题 问题: 起点s, 终点t, 途中经过的管道(边)都有一个最大流量, s到t的最大水流量是多少? 定义: 残量网络 => 当前管道还可以容纳的水量(随着搜索变化), 加入反向边, 如果寻找到的增广路是错误路径, 可以通过反向边修正. 增广路算法: BFS找最短的增广路径, 修改这条路径流量值(修改残量网络边权), 递归此步骤.

增广路(Ford-Fulkerson)算法

import copy
from queue import Queue

INF = 400

N = 6

G = [
    [ 0, 5, 5, 0, 0, 0],
    [ 0, 0, 0, 5, 5, 0],
    [ 0, 0, 0, 5, 0, 0],
    [ 0, 0, 0, 0, 0, 5],
    [ 0, 0, 0, 0, 0, 5],
    [ 0, 0, 0, 0, 0, 0]
]

def find_path(R, P):
    V = [False for i in range(N)]
    Q = Queue()
    Q.put(S)
    V[S] = True

    # BFS find_path
    while not Q.empty():
        top = Q.get()
        for i in range(N):
            if not V[i] and R[top][i]>0:
                Q.put(i)
                V[i] = True
                P[i] = top

    # if find vertex T
    return V[T] == True


def FF():
    # R => Rest Flow Graph(Use Reverse R)
    R = copy.deepcopy(G)
    max_flow = 0

    # P => Path Of Flow
    P = [0 for i in range(N)]

    # if has path
    while find_path(R, P):
        min_flow = INF

        # find min flow
        v = T
        while v != S:
            u = P[v]
            min_flow = min(min_flow, R[u][v])
            v = P[v]
        
        # adjust R
        v = T
        while v != S:
            u = P[v]
            R[u][v] -= min_flow
            # reverse path => fix prior error path
            R[v][u] += min_flow
            v = P[v]

        # add max flow
        max_flow += min_flow

    return max_flow


S, T = 0, N-1

max_flow = FF()

print(max_flow)

猜你喜欢

转载自my.oschina.net/u/3655970/blog/1536109