Некоторые задачи
Здесь будут публиковаться разборы некоторых из задач из списка.
Разборы, приведённые здесь, отличаются от тех, что рассматривались на уроках: вместо заполнения плотной матрицы смежности здесь мы будем заполнять разреженную.
Программный интерфейс разреженной матрицы смежности
Для того, чтобы общаться с графом, мы будем использовать следующий интерфейс:
def empty_graph(n):
return { (x+1): {} for x in range(n) }
def graph_size(g):
return len(g)
def add_edge(g, v1, v2):
if v2 in g[v1]: g[v1][v2] += 1
else: g[v1][v2] = 1
def set_edge(g, v1, v2, e):
g[v1][v2] = e
def get_edge(g, v1, v2):
if v2 in g[v1]: return g[v1][v2]
return 0
def neighbors(g, v):
return [(n, g[v][n]) for n in g[v]]
Этот интерфейс отличается от edges_of_vertices
/neighbors
из
предыдущего раздела. Основные отличия:
- вершинами являются не все целые числа, а только конечный их отрезок, начинающийся с единицы
- в матрице хранятся не сами рёбра, а только их кратности
neighbors
возвращает не только соседние вершины, но и кратности соответствующих рёбер
Ввод-вывод графов
Это по большей части задачи с E по H. Начнём с двух способов печати графа: как квадратной таблицы и как списка рёбер.
def print_graph_matrix(g):
N = graph_size(g)
for y in range(N):
print(' '.join([
str(get_edge(g, y+1,x+1)) for x in range(N)
]))
def print_graph_edges(g, directed=False):
N = graph_size(g)
for i in range(N):
v = i+1
for x,e in neighbors(g,v):
if directed or v <= x:
for _ in range(e): print(str(v), str(x))
Если не удалось понять, как работает вышеприведённая функция print_graph_matrix
,
предлагаем альтернативную реализацию:
def print_graph_matrix(g):
N = graph_size(g)
for y in range(N):
for x in range(N):
end = ' ' if x < N-1 else '\n'
print(get_edge(g, y+1, x+1), end=end)
Теперь рассмотрим ввод графов в таком виде:
def read_graph_matrix_with_sizes():
return read_graph_matrix(int(input()))
def read_graph_matrix(N):
g = empty_graph(N)
for i in range(N):
numbers = [int(x) for x in input().split()]
for j,e in enumerate(numbers):
if e > 0: set_edge(g, i+1, j+1, e)
return g
def read_graph_edges_with_sizes(directed=False):
N, E = [int(x) for x in input().split()]
return read_graph_edges(N,E,directed)
def read_graph_edges(N,E,directed=False):
g = empty_graph(N)
for i in range(E):
v1, v2 = [int(x) for x in input().split()]
add_edge(g, v1, v2)
if not directed: add_edge(g, v2, v1)
return g
В терминах этих функций задачи E, F, G, H решаются так:
def E():
print_graph_edges(read_graph_matrix_with_sizes())
def F():
print_graph_matrix(read_graph_edges_with_sizes())
def G():
print_graph_edges(read_graph_matrix_with_sizes(), True)
def H():
print_graph_matrix(read_graph_edges_with_sizes(True))
Ещё несколько задач
Задача B: есть ли в графе петли?
def B():
if has_loops(read_graph_matrix_with_sizes()):
print("YES")
else:
print("NO")
def has_loops(g):
N = graph_size(g)
for v in range(1,N+1):
if get_edge(g, v, v) > 0: return True
return False
Задача C: количество рёбер неориентированного графа.
def C():
print(count_edges(read_graph_matrix_with_sizes()))
def count_edges(g):
N = graph_size(g)
total = 0
for v in range(1,N+1):
for x,count in neighbors(g,v):
if x >= v: total += count
return total
Задача I: разворот рёбер ориентированного графа, заданного списками смежности.
def read_inverted_graph_lists(N):
g = empty_graph(N)
for i in range(N):
for x in input().split():
v = int(x)
add_edge(g, v, i+1) ## разворачиваем сразу при добавлении
return g
def print_graph_lists(g):
N = graph_size(g)
for i in range(N):
v = i+1
for x,_ in sorted(neighbors(g,v)):
print(x, end=' ')
print()
def I():
N = int(input())
g = read_inverted_graph_lists(N)
print(N)
print_graph_lists(g)