Detetando Ciclos em um Grafo

Detetar ciclos em grafos é essencial para compreender a estrutura e as propriedades dos dados em vários domínios, como gestão de dependências, deteção de deadlocks e roteamento de redes. Ao identificar ciclos, conseguimos evitar problemas como dependências circulares, ineficiências e até falhas de sistema.

Uma versão modificada do DFS pode ser usada para detetar um ciclo em um grafo.

profound.academy-dfs-cycle.drawio.png

Ao percorrer um grafo com DFS, normalmente marcamos os vértices como visitados ou não visitados. Ao detetar ciclos, isso não é suficiente. Precisamos de 3 estados para cada vértice:

  1. Vértice não visitado

  2. Vértice e respetiva subárvore DFS em processamento

  3. Vértice totalmente visitado (com todos os nós filhos)

Assim, podemos detetar um ciclo se, ao percorrer o grafo, chegarmos a um vértice que esteja atualmente em processamento.

O algoritmo executa os seguintes passos:

  1. Marcar todos os vértices como não visitados

  2. Iniciar um DFS a partir de algum vértice

  3. Para o vértice atual, marcar como em processamento

  4. Quando todos os nós filhos do vértice atual estiverem processados, marcar o vértice como totalmente visitado

O algoritmo pode ser aplicado tanto em grafos dirigidos como em grafos não dirigidos:

color = [1] * n                             # cor dos vértices (1: não visitado, 2: em processamento, 3: totalmente visitado)

def dfs(v, p):                              # depth-first search (v: vértice, p: pai)
    color[v] = 2                            # marcar vértice como em processamento
    cycle = False                           # indicador para verificar se existe ciclo

    for to in g[v]:                         # para cada vértice ligado a v
        if color[to] == 1:                  # se o vértice não foi visitado
            cycle |= dfs(to, v)             # chamar dfs no vértice e assinalar se existe ciclo
        elif color[to] == 2 and to != p:    # se o vértice está em processamento e não é o pai
            cycle = True                    # marcar indicador como True
    color[v] = 3                            # marcar vértice como totalmente visitado
    return cycle                            # retornar indicador


for start in range(n):                      # para cada vértice
    if color[start] == 1:                   # se o vértice não foi visitado
        if dfs(start, -1):                  # chamar dfs no vértice e verificar se há ciclo
            print('Cycle')                  # imprimir ciclo
            break                           # interromper o loop
else:                                       # se o loop não foi interrompido por um break
    print('No cycle')                       # imprimir que não há ciclo

Observe que, para grafos direcionados, levar em conta o vértice-pai não é necessário. Consegue dizer porquê 🤔?

Vamos simular o algoritmo em vários exemplos de entrada:

n = 4, e = 4

Os pares de entrada a, b são os seguintes: [(0, 1), (0, 2), (1, 2), (3, 2)]

O vértice inicial é 3

  1. v=3, p=-1color = [1, 1, 1, 2] ⇒ explorar todos os vizinhos de 3 ⇒ [2]

  2. v=2, p=3color = [1, 1, 2, 2] ⇒ explorar todos os vizinhos de 2 ⇒ [0, 1]

  3. v=0, p=2color = [2, 1, 2, 2] ⇒ explorar todos os vizinhos de 0 ⇒ [1, 2]

  4. v=1, p=0color = [2, 2, 2, 2] ⇒ explorar todos os vizinhos de 1 ⇒ [0, 2] ⇒ 0 está marcado como 1 ⇒ marcar cycle = Truereturn True

Desafio: Verificar se um grafo contém um ciclo

Dado um grafo direcionado com v vértices e e arestas, a sua tarefa é verificar se o grafo contém um ciclo.

Entrada

A primeira linha da entrada contém dois inteiros v (1 ≤ v ≤ 100 000) e e (1 ≤ e ≤ 100 000).

As seguintes e linhas contêm pares de inteiros v1, v2 (1 ≤ v1, v2 ≤ v) que significam que existe um caminho do vértice v1 para o vértice v2.

Saída

O programa deve imprimir Yes se o grafo tiver um ciclo e No caso contrário.

Exemplos

Input

Output

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