No começo: é um problema de concurso de programação, mas não de um problema em andamento. Infelizmente, não posso fornecer nenhum link para esta tarefa, porque ela não está disponível publicamente. Era de um concurso de programação local polonês em 2011, organizado pela Escola de Ciência da Computação de Varsóvia.
Eu tenho um gráfico com vértices sem arestas e uma lista de bordas direcionadas. Nosegundo -a aresta está sendo adicionada a um gráfico. Quero saber em que segundo haverá um ciclo em um gráfico.
A solução mais óbvia seria executar o DFS após adicionar cada borda, mas seria necessário Tempo. Outra solução seria manter o gráfico classificado topologicamente e, ao adicionar uma aresta, eu poderia colocá-lo de maneira a não perturbar a ordem topológica. Isso levaria no máximoTempo. Eu fiz algumas pesquisas no Google e parece que esse é o algoritmo online mais rápido.
Como conheço todas as bordas de antemão, posso usar o algoritmo offline. O algoritmo offline mais rápido que consigo pensar é na pesquisa binária. Se depois- o segundo segundo gráfico contém um ciclo; obviamente, ele terá um ciclo em qualquer outro segundo . Para que eu possa fazer pesquisa binária para encontrar as menores executando o DFS vezes, cada um deles tempo, então a complexidade total dessa solução seria .
É bastante rápido, mas gostaria de saber se existe algum algoritmo offline mais rápido. Uma coisa que consigo pensar é atribuir a cada peso de borda igual ao momento em que essa borda foi adicionada a um gráfico. Então a tarefa seria equivalente a encontrar um ciclo com o peso máximo mínimo da borda. Não sei se isso leva a algum lugar.
fonte
Respostas:
Algoritmo do KWillets
O KWillets parece ter o melhor algoritmo . Basicamente, você alterna iterativamente entre as duas etapas a seguir:
Para cada fonte (vértice sem aresta de saída), remova a fonte e todas as arestas que saem dela. Repita até que não haja mais fontes.
Remova a aresta com o número mais alto, ou seja, a aresta mais recente (de todas as arestas restantes). Volte para a etapa 1.
Quando o gráfico estiver vazio, desfaça a última etapa 1 e a etapa 2 e o gráfico mais antigo com um ciclo. Em outras palavras, pegue a última aresta removida na última etapa 2 antes que o gráfico fique vazio, e o gráfico que contém essa aresta e todas as arestas anteriores é o primeiro que contém um ciclo.
Como sua lista de arestas já está classificada com o aumento do tempo, isso pode ser implementado emO(V+E) Tempo. Você manterá a lista de arestas em uma lista duplamente vinculada, com ponteiros entre esses nós e a aresta correspondente no gráfico. Etapa 1 requerO(1) tempo por nó ou borda excluída, se você mantiver uma lista de trabalho separada de todos os vértices de origem e sempre que excluir uma borda, verificará se isso criou um novo vértice de origem.
Pesquisa binária
É possível fazer algumas pequenas otimizações no seu método de pesquisa binária, embora elas não melhorem o tempo de execução assintótica: o tempo de execução assintótica ainda seráO((V+E)logE) .
Suponha que você analise o gráfico no momentok e descubra que ele contém um ciclo. Decomponha o gráfico em componentes fortemente conectados. Agora você pode excluir permanentemente todos os nós isolados (ou seja, nós que estão em um SCC de tamanho 1); eles não podem fazer parte de um ciclo. Você pode continuar a pesquisa binária a partir daqui.
Além disso, quando você se decompõe em componentes fortemente conectados, para cada componente fortemente conectado, faça o seguinte. Conte o número de nós no SCC, digamosn0 deles. Classifique as arestas no SCC com o aumento do tempo. Veja a hora don0 th deles, diga o tempo j0 . Então a primeira vez que houver um ciclo dentro desse componente fortemente conectado deve ser o tempoj0 ou mais tarde. Faça isso para cada componente fortemente conectado e realize o mais cedo possível. Então, como uma otimização, não há necessidade de considerar nenhum momento anterior à pesquisa binária.
No entanto, essas duas otimizações provavelmente não fornecerão mais do que uma pequena aceleração de fator constante, na melhor das hipóteses.
fonte
O algoritmo de Kahn é conhecido pela classificação topológica, mas também pode ser usado para encontrar componentes fortemente conectados. Eu o uso para verificação de ciclo, onde uma classificação topológica real não é necessária. Nesta variante, os nós são descartados à medida que são visitados, em vez de serem adicionados à saída, e a terminação do erro indica pelo menos um ciclo.
Ele funciona removendo os nós de raiz ou folha (tendo 0 grau in ou out 0). Do gráfico, um de cada vez, até que não sejam encontrados mais nós desse tipo. Como esse processo não pode cortar um ciclo (todos os nós de um ciclo têm indegree e outdegree> 0), ele termina com um gráfico não vazio se houver pelo menos um ciclo.
Se cortarmos a aresta máxima restante e em cada um desses pontos de terminação, podemos continuar o processo para ver se E \ e ainda contém um ciclo, e assim por diante, iterativamente, até que cortemos arestas suficientes para tornar E acíclico. O último corte de borda k é o primeiro k que torna E [1 ... k] cíclico.
O algoritmo de Kahn realiza esse processo de corte linear de maneira linear, usando uma fila de trabalho de nós a serem excluídos; ele é inicializado com as folhas do gráfico inteiro (pelo menos um deve existir, se for acíclico) e, conforme cada uma é excluída, seu (s) nó (s) pai (s) é verificado para ver se, por sua vez, se tornam folhas e, se for o caso eles são adicionados à fila de trabalho. Ao verificar a vizinhança de cada exclusão, ele funciona linearmente através do gráfico, sem a necessidade de estruturas de indexação além da fila FIFO.
Podemos fazer a mesma verificação de folga do vizinho quando cortamos a borda máxima e adicionar as folhas criadas à fila de trabalho ou, se nenhuma for criada, continuar cortando.
Para tornar o processo de corte máximo linear, precisamos encontrar a aresta máxima não excluída por meio de uma varredura descendente da matriz de arestas, mas cada varredura começa onde a anterior parou e o algoritmo faz apenas uma única passagem pela matriz inteira, de modo que a parte seja O (E) no total. No geral, acredito que esse método é O (V + E), o mesmo que o de Kahn.
fonte