Լայնությամբ որոնման (Breadth First Search, BFS) ալգորիթմ
Պատկերացրեք, որ ունեք գրաֆ, որը բաղկացած է n գագաթից և e եզրից, և ցանկանում եք սկսել որևէ գագաթից ու աստիճանաբար անցնել բոլոր գագաթներով, որոնք հասանելի են անվանած մեկնարկային գագաթից։ Կան մի քանի տարբերակներ դա անելու համար, սակայն ամենահայտնի մոտեցումներից մեկը հենց Լայնությամբ որոնման (Breadth-First Search, BFS) ալգորիթմն է։ BFS-ի բնույթը թույլ է տալիս գտնել ամենակարճ ճանապարհը ելքային (source) գագաթից դեպի մնացած բոլոր գագաթները, ինչն այն չափազանց օգտակար է դարձնում տարբեր փուլերում և խնդիրներում։
Լավ պատկերասարքման համար կարելի է օգտագործել «կրակով այրման» համեմատությունը։ Պատկերացրեք, որ յուրաքանչյուր գագաթ “այրվում է” 1 րոպե, հետո կրակը տարածվում է նրա անմիջական հարևաններին։ Երբ հարևանները նույնպես “այրվում են” 1 րոպե, կրակը նորից շարունակվում է դեպի նրանց հարևանները և այլն։
Այսինքն, նախ “վառում” եք մեկնարկային գագաթը, ապա 1 րոպե անց կրակը տարածվում է նրա բոլոր հարևաններին։ercial նրանց այրումից հետո այն անցնում է նրանց հարևաններին, և այդպես շարունակ, մինչև ոչ մի гագաթ այլևս չի մնա անεκած։ Այս տեսողական մեթոդը հաճախ հեշտացնում է BFS ալգորիթմի իրագործումը:
BFS-ի իրագործման համար կարելի է օգտագործել queue և այն գագաթների ցուցակը, որոնք արդեն “այրվում են” (used):
from collections import deque
n, e = map(int, input().split()) # n: vertices, e: edges
g = [[] for _ in range(n)] # graph - adjacency list
for _ in range(e): # read edges
a, b = map(int, input().split()) # a -> b and b -> a
g[a].append(b)
g[b].append(a)
start = int(input()) # start vertex
used = [False] * n # used vertices (visited)
q = deque([start]) # queue of vertices
used[start] = True # mark start vertex as visited
while q: # while the queue is not empty
v = q.popleft() # get vertex from queue
print(v, end=' ') # print vertex
for to in g[v]: # for each vertex connected to v
if not used[to]: # if vertex is not visited
q.append(to) # add vertex to queue
used[to] = True # mark vertex as visited
Այստեղ նախ կազմվում է գրաֆը, ապա լայնությամբ որոնումն սկսվում է start գագաթից։ Սկզբում used զանգվածի բոլոր արժեքները False են։ Յուրաքանչյուր գագաթ այցելելուց հետո այն ավելացվում է հերթ (queue) և նշվում է որպես օգտագործված (used)։ Սա նման է «գրաֆը վառելու» գաղափարին։ Յուրաքանչյուր քայլում, վերցնելով հերթից текущий գագաթը, նրա բոլոր չօգտագործված հարևանները նույնպես ավելացվում են հերթ, ու դրանով նրանք նույնպես «վառվում են»։
Եկեք նայենք, թե ինչպես է ալգորիթմը գործում մի քանի օրինակների վրա:
n = 4, e = 4
Մուտքային (a, b) զույգերն են: [(0, 1), (0, 2), (1, 2), (3, 2)]
Մեկնարկային գագաթը 3-ն է
used = [False, False, False, True], q = [3]
v = 3, q = [] → նրա չօգտագործված հարևան(g)ները ավելացվում են q
used = [False, False, True, True], q = [2]
v = 2, q = [] → ավելացվում են նրա չօգտագործված հարևան(g)ները
used = [True, True, True, True], q = [0, 1]
v = 0, q = [1] → ավելացվում են նրա չօգտագործված հարևան(g)ները
v = 1, q = [] → ավելացվում են նրա չօգտագործված հարևան(g)ները
Ավարտ
n = 5, e = 4
Մուտքային (a, b) զույգերն են: [(0, 1), (0, 2), (1, 2), (3, 4)]
Մեկնարկային գագաթը 0-ն է
used = [True, False, False, False, False], q = [0]
v = 0, q = [] → նրա չօգտագործված հարևան(g)ները ավելացվում են q
v = 1, q = [2] → նրա չօգտագործված հարևան(g)ները ավելացվում են q
v = 2, q = [] → նրա չօգտագործված հարևան(g)ները ավելացվում են q
Ավարտ
Notice how nodes 3 and 4 were never visited.
They were not connected to the initial node 0.
Առաջադրանք: Հաշվել գրաֆի կապակցված կոմպոնենտների քանակը
Տրված է անուղղորդ գրաֆ, որն ունի v գագաթ և e եզր։ Պահանջվում է հաշվել, թե քանի կապակցված կոմպոնենտ կա գրաֆում։ Կապակցված կոմպոնենտ ենք համարում այն գագաթների համախումբը, որոնց միջև կարելի է տեղաշարժվել։
Մուտք
Մուտքի առաջին տողում տրված են երկու ամբողջ թվեր v (1 ≤ v ≤ 100 000) և e (1 ≤ e ≤ 100 000)։
Հաջորդ e տողերում տրված են զույգեր v1, v2 (1 ≤ v1, v2 ≤ v), որոնք մատնանշում են, որ գագաթ v1-ը կապակցված է գագաթ v2-ի հետ և հակառակը։
Ելք
Ծրագիրը պետք է տպի, թե քանի կապակցված կոմպոնենտ կա գրաֆում։