В полном графе из N вершин N(N - 1)/2 рёбер. Соберём рёбра в список и случайно его перемешаем.
Заведём граф без ребёр и будем добавлять в него рёбра из списка по одному. После каждого добавления будем подсчитывать число компонент связности. Оно не возрастает, уменьшается с шагом единица. Рано или поздно получим K компонент. В этот момент запомним индекс ребра i. Продолжим пока не станет K - 1 компонента. Запомним индекс j. Случайно выберем число ребёр из [i, j). Построим матрицу используя это число рёбер. Задача решена.
Этим способом может быть сгенерирован любой возможный граф из N вершин с K компонентами связности.
Сложность O(N2) если для подсчёта компонент связности воспользоваться системой непересекающихся множеств. Код ниже реализует эту программу:
import random
def components(n, edges):
parent = list(range(n))
rank = [0] * n
def find_set(i):
if parent[i] != i:
parent[i] = find_set(parent[i])
return parent[i]
def connect(i, j):
assert i != j
assert parent[i] == i
assert parent[j] == j
if rank[i] > rank[j]:
parent[j] = i
else:
parent[i] = j
if rank[i] == rank[j]:
rank[i] += 1
yield 0
for k, (i, j) in enumerate(edges, start=1):
i = find_set(i)
j = find_set(j)
if i != j:
connect(i, j)
yield k
yield len(edges)
def print_matrix(n, edges):
m = [[0] * n for _ in range(n)]
for i, j in edges:
m[i][j] = 1
m[j][i] = 1
for row in m:
print(*row)
def main():
n, k = map(int, input().split())
edges = [(i, j) for j in range(n) for i in range(j)]
random.shuffle(edges)
it = components(n, edges)
for _ in range(n - k):
next(it)
i = next(it)
j = next(it)
print_matrix(n, edges[:random.randrange(i, j)])
main()
P.S. Давно мечтал применить disjoint-set-union. Вот получилось.