Գրաֆում ցիկլերի հայտնաբերում

Գրաֆում ցիկլերի հայտնաբերումը կարևոր է տվյալների կառուցվածքն ու հատկությունները հասկանալու համար բազմազան ոլորտներում, օրինակ՝ կախվածությունների կառավարում, փակուղային վիճակների (deadlock) հայտնաբերում, ինչպես նաև ցանցային երթուղավորում։ Ցիկլերը հայտնաբերելով՝ կարող ենք կանխել շրջանային կախվածությունները, անարդյունավետությունները կամ նույնիսկ համակարգի ձախողումները։
Գրաֆում ցիկլ հայտնաբերելու համար կարելի է օգտագործել DFS-ի փոփոխված տարբերակը։
notion image
Գրաֆը DFS-ով遍անցելիս սովորաբար գագաթները բաժանում ենք «այցելված» կամ «չայցելված» վիճակների։ Սակայն ցիկլերն հայտնաբերելու համար դա բավական չէ։ Անհրաժեշտ է յուրաքանչյուր գագաթի համար պահպանել 3 վիճակ.
  1. Գագաթը դեռ չի այցելվել
  1. Գագաթը եւ դրա DFS ծառակազմը մշակման մեջ են
  1. Գագաթը ամբողջությամբ այցելված է (ներառյալ բոլոր որդի գագաթները)
Այս եղանակով, եթեTraversal-ի ժամանակ հասնենք մի գագաթի, որը հենց հիմա մշակման մեջ է, ուրեմն ցիկլ ունենք։
ԵթեTraversal-ի ընթացքում հասնում ենք մի գագաթի, որը նշված է որպես մշակման մեջ գտնվող (2), ապա գտել ենք ցիկլ։
Ալգորիթմը կատարում է հետևյալ քայլերը.
  1. Նշել բոլոր գագաթները որպես չայցելված
  1. Սկսել DFS որևէ գագաթից
  1. Հ текущ гագաթի համար այն նշել որպես մշակման մեջ գտնվող
  1. Երբ տվյալ գագաթի բոլոր որդի գագաթները մշակված են, նշել այն որպես ամբողջությամբ այցելված
Ալգորիթմը հնարավոր է կիրառել և՛ ուղղորդված (directed), և՛ ոչ ուղղորդված (undirected) գրաֆերում.
color = [1] * n                             # գագաթների գույները (1: չայցելված, 2: մշակման մեջ գտնվող, 3: ամբողջությամբ այցելված)

def dfs(v, p):                              # խորության առաջին որոնում (v: գագաթ, p: ծնող)
    color[v] = 2                            # նշում ենք գագաթը որպես մշակման մեջ գտնվող
    cycle = False                           # փոփոխական, որը ցույց է տալիս արդյոք կա ցիկլ

    for to in g[v]:                         # v-ին կապված յուրաքանչյուր գագաթի համար
        if color[to] == 1:                  # եթե գագաթը չայցելված է
            cycle |= dfs(to, v)             # կանչում ենք dfs՝ ստուգելով արդյոք կա ցիկլ
        elif color[to] == 2 and to != p:    # եթե գագաթը մշակման մեջ է և դա ծնողը չէ
            cycle = True                    # նշում ենք, որ ունենք ցիկլ
    color[v] = 3                            # նշում ենք գագաթը որպես ամբողջությամբ այցելված
    return cycle                            # վերադարձնում ենք նշված փոփոխականը


for start in range(n):                      # յուրաքանչյուր գագաթի համար
    if color[start] == 1:                   # եթե գագաթը չայցելված է
        if dfs(start, -1):                  # կանչում ենք dfs և ստուգում արդյոք կա ցիկլ
            print('Cycle')                  # տպում ենք 'Cycle'
            break                           # դիմումից դուրս ենք գալիս
else:                                       # եթե ցիկլ չի գտնվել և break չի կատարվել
    print('No cycle')                       # տպում ենք 'No cycle'
Նկատի ունեցեք, որ ուղղորդված գրաֆերի դեպքում «parent» պարամետրը հաշվի առնել պետք չէ։ Գիտե՞ք, թե ինչու 🤔?
 
Եկեք իրականացնենք ալգորիթմի քայլերը մի քանի մուտքերի վրա.
n = 4, e = 4
Մուտքային a, b զույգերն են՝ [(0, 1), (0, 2), (1, 2), (3, 2)]
Սկիզբը վերցնենք 3-ից
  1. v=3, p=-1color = [1, 1, 1, 2] ⇒ ուսումնասիրում ենք 3-ի հարևաններին ⇒ [2]
  1. v=2, p=3color = [1, 1, 2, 2] ⇒ ուսումնասիրում ենք 2-ի հարևաններին ⇒ [0, 1]
  1. v=0, p=2color = [2, 1, 2, 2] ⇒ ուսումնասիրում ենք 0-ի հարևաններին ⇒ [1, 2]
  1. v=1, p=0color = [2, 2, 2, 2] ⇒ ուսումնասիրում ենք 1-ի հարևաններին ⇒ [0, 2] ⇒ 0 գագաթը նշված է որպես 1 ⇒ այդտեղ նշում ենք cycle = Truereturn True

Առաջադրանք: Ստուգել, արդյոք գրաֆը պարունակում է ցիկլ

Տրված է ուղղորդված գրաֆ v գագաթներով և e կողերով։ Պահանջվում է ստուգել, թե արդյոք գրաֆը պարունակում է ցիկլ։

Մուտք

Մուտքի առաջին տողում տրված են երկու ամբողջ թվեր v (1 ≤ v ≤ 100 000) և e (1 ≤ e ≤ 100 000)։
Հաջորդ e տողերում տրված են զույգեր v1, v2 (1 ≤ v1, v2 ≤ v), որոնք ցույց են տալիս, որ գագաթ v1-ից կա ուղի դեպի v2 գագաթը։

Ելք

Ծրագիրը պետք է տպի Yes, եթե գրաֆում կա ցիկլ, և No հակառակ դեպքում։

Օրինակներ

Մուտք
Ելք
4 3 1 2 2 3 3 1
Yes
4 3 1 2 1 3 2 4
No
 

Constraints

Time limit: 2 seconds

Memory limit: 512 MB

Output limit: 1 MB

To check your solution you need to sign in
Sign in to continue