【Algoritmo de caminho mais curto】SPFA

introduzir

No mundo da ciência da computação, os algoritmos são como estrelas no céu estrelado, cada uma brilhando com a luz da sabedoria. Eles são silenciosos e firmes, como um grupo de filósofos silenciosos, respondendo silenciosamente aos problemas do mundo.

As etapas do algoritmo são como belos versos de poesia, permitindo que problemas complexos sejam lançados nos caracteres que fluem. Eles são como fontes claras nas montanhas, fluindo de um pico a outro, lavando a poeira dos problemas e revelando a verdadeira face.

São como chaves que abrem a porta para a informática. Nós os usamos para resolver problemas, nós os usamos para fazer milagres. Eles são a cristalização de nossa sabedoria, nossa compreensão do mundo e nossa visão para o futuro.

Entre neste mundo cheio de algoritmos e sinta a luz da sabedoria e do ritmo poético. Vamos juntos explorar o território desconhecido, em busca das mais belas paisagens e do tesouro mais precioso.

Algoritmos não são apenas a base da ciência da computação, mas também a poesia de nossas vidas. Deixam-nos ver a esperança do futuro e sentir o encanto da tecnologia. Então, vamos abraçar os algoritmos juntos, deixá-los adicionar cor às nossas vidas e trazer mais possibilidades ao nosso mundo.

Algoritmos são como iguarias em ciência da computação, cada um com seu sabor e sabor únicos. Alguns algoritmos são como delicadas sobremesas francesas, complexas e delicadas, exigindo que as provemos uma a uma pacientemente; alguns algoritmos são como simples pão rústico, simples e prático, fazendo as pessoas se sentirem cordiais e calorosas.

introdução básica

E neste vasto mar de algoritmos, existe um algoritmo que brilha intensamente - o algoritmo do caminho mais curto.

O algoritmo de caminho mais curto é um algoritmo de teoria de grafos para encontrar o caminho mais curto entre dois nós em um grafo ponderado. Este algoritmo tem uma vasta gama de aplicações na vida real, tais como:

  1. Planejamento de tráfego: o algoritmo de caminho mais curto pode ser usado no planejamento de tráfego urbano para ajudar a determinar a rota mais curta para reduzir o congestionamento e melhorar a eficiência do tráfego.
  2. Distribuição logística: Na distribuição logística, o algoritmo do caminho mais curto pode ajudar a determinar o caminho mais curto para minimizar os custos e o tempo de transporte.
  3. Projeto de Rede: Em redes de computadores, os algoritmos de caminho mais curto podem ajudar a determinar a melhor rota para garantir que os pacotes de dados sejam transmitidos o mais rápido possível.
  4. Sistemas de Informação Geográfica: Em sistemas de informação geográfica, o algoritmo de caminho mais curto pode ser usado para determinar o caminho mais curto entre dois pontos, como encontrar a melhor rota entre dois pontos em um mapa.

Entre eles, o SPFA é a chave para abrir a porta da teoria dos grafos, vamos entrar nela.

linha de raciocínio

O nome completo do algoritmo SPFA é: Shortest Path Faster Algorithm, que é o nome em um artigo publicado por Duan Fanding da Southwest Jiaotong University em 1994. No entanto, a prova de Duan Fending está errada, e logo após o algoritmo de Bellman-Ford ser proposto (em 1957), havia conteúdo de otimização de filas , então o algoritmo SPFA não é reconhecido internacionalmente como o de Duan Fending.

Para evitar o pior caso, o algoritmo de Dijkstra mais eficiente deve ser usado no gráfico de peso positivo .

Se houver arestas de peso negativo em um determinado gráfico, algoritmos como o algoritmo de Dijkstra serão inúteis e o algoritmo SPFA será útil. Por uma questão de brevidade, concordamos que não há loop de peso negativo no grafo direcionado ponderado G, ou seja, o caminho mais curto deve existir. Um array d é usado para registrar o valor estimado do caminho mais curto de cada nó , e uma lista de adjacências é usada para armazenar o grafo G. O método que adotamos é o método de aproximação dinâmica: configure uma fila primeiro a entrar, primeiro a sair para salvar os nós a serem otimizados, retire o nó principal u a cada vez durante a otimização e use o valor atual de estimativa de caminho mais curto do ponto u para sair do ponto u O nó v apontado executa uma operação de relaxamento . Se o valor estimado do caminho mais curto do ponto v for ajustado e o ponto v não estiver na fila atual, o ponto v será colocado no final da fila . Desta forma, os nós são continuamente retirados da fila para realizar operações de relaxamento até que a fila esteja vazia.

Teorema: Enquanto existir o caminho mais curto, o algoritmo SPFA acima deve ser capaz de encontrar o valor mínimo . Prova: Sempre que um ponto é colocado no final da fila, isso é obtido por meio de uma operação de relaxamento . Em outras palavras, cada otimização fará com que o valor d[v] da estimativa do caminho mais curto de um determinado ponto v se torne menor. Portanto, a execução do algoritmo tornará d cada vez menor. Como assumimos que não há ciclos ponderados negativamente no grafo, cada nó tem um valor de caminho mais curto. Portanto, o algoritmo não será executado indefinidamente.Como o valor de d diminui gradativamente, o algoritmo termina quando o valor do caminho mais curto é atingido, e o valor estimado do caminho mais curto neste momento é o valor do caminho mais curto do nó correspondente.

De fato, se um ponto entrar na fila até n vezes, isso indica que há um ciclo negativo no grafo, e não há caminho mais curto.

A prova de complexidade (O(kE), k é uma pequena constante) na tese de Duan Fending está errada e é omitida aqui. A pior complexidade de tempo deste algoritmo é O(VE).

Uma compreensão muito intuitiva do SPFA é transformada a partir do BFS do gráfico não ponderado. Em um grafo não ponderado, o caminho experimentado pelo primeiro vértice alcançado pelo BFS deve ser o caminho mais curto (ou seja, o número mínimo de vértices passados), portanto, neste momento, usar uma matriz para registrar o acesso do nó pode fazer com que cada vértice entre no fila apenas uma vez, mas na banda No grafo da direita, o caminho calculado pelo primeiro vértice alcançado não é necessariamente o caminho mais curto. Uma solução é abandonar o array. O tempo necessário neste momento é naturalmente exponencial , então não podemos abandonar o array, mas atualizar diretamente o caminho ótimo ao processar um vértice que já está na fila e o caminho atual é melhor que o original .solução . _

O algoritmo SPFA possui quatro estratégias de otimização: otimização de pilha, otimização de pilha, SLF e LLL.

  • Otimização do heap : substitua a fila por um heap.A diferença de Dijkstra é que um ponto pode entrar no heap várias vezes. Gráficos com arestas ponderadas negativamente podem ficar presos em complexidade exponencial.

  • Otimização da pilha : substitua a fila por uma pilha (ou seja, altere o processo BFS original para  DFS ), o que pode ser mais eficiente ao procurar por anéis negativos, mas a pior complexidade de tempo ainda é exponencial.

  • SLF: estratégia Small Label First, assumindo que o nó a ser adicionado é j, e o elemento principal da equipe é i, se dist(j)<dist(i), insira j no início da equipe, caso contrário insira na cauda da equipe;

  • LLL: Large Label Última estratégia, defina o primeiro elemento da fila como i, o valor médio de todos os valores dist na fila é x, se dist(i)>x, insira i no final da fila, pesquise para o próximo elemento até que um certo i seja encontrado Se dist(i)<=x, i será retirado da fila para operação de relaxamento.

As otimizações SLF e LLL funcionam bem em dados aleatórios, mas o pior caso é O(VE) em gráficos de peso positivo, e o pior caso é a complexidade exponencial em gráficos de peso negativo.

Em última análise, SPFA é Kuansou, basta olhar para ele.

#include <bits/stdc++.h>
using namespace std;
struct edge{int x, y, c, pre;} a[410000];int alen, last[11100];
void ins(int x, int y, int c)//ins函数的功能是建立一条从x出发到y且长度为c的边
{
    a[++alen] = edge{x, y, c, last[x]}; 全局增加一条有向边,并赋值
    last[x] = alen;                     //建立边与边的联系(都是从x出发)
}

int n, m, d[11100]; //d[i]表示目前i和出发点的最短距离
bool v[11100];      //v[i]等于true表示点i在更新队列中,等于false表示点i不在更新队列中
void spfa()
{
    memset(d, 63, sizeof(d));d[1] = 0; //初始化d数组,出发点为1,出发点到自己的距离为0
    memset(v, 0, sizeof(v)); v[1] = 1; //初始化v数组,v[1]等于1,表示出发点1进入队列
    deque<int> Q; Q.push_back(1);   //定义更新队列,出发点进入更新队列
    while (!Q.empty()) //只要队列不为空,就表示还有点等着更新别的点
    {
        int x = Q.front();                     //从队列中取出准备好更新别的点的点x
        for (int k = last[x]; k; k = a[k].pre) //重点理解!k首相等于和x相连的最后一条边的编号。那么倒数第二条和x相连的边的编号是多少呢?在a[k].next可以找到
        {
            int y = a[k].y;
            if (d[y] > d[x] + a[k].c) //尝试用x的最短距离更新y的最短距离
            {
                d[y] = d[x] + a[k].c; //如果点被更新了,那么它马上有冲动(想进更新队列)要去更新它的"亲朋好友"(与之直接相连的点)
                if (v[y] == 0)        //如果点y不在队列,则进入队列;如果已经在队列了,则不用再进入。
                {
                    Q.push_back(y);
                    v[y] = 1;
                }
            }
        }
        Q.pop_front(); //此时x已经更新完它的"亲朋好友",完成使命,退出队列Q
        v[x] = 0;      //标记x已经不再队列,以后x有可能会再次进入队列
    }
    //队列没有点等着更新别的点了,那么意味着所有点的d值都是最优的
    if (d[n] == d[0]) printf("-1\n"); //d[0]为初始的最大值,d[n]等于d[0]表示点n没有被更新,即点n无法到达。
    else              printf("%d", d[n]);
}
int main()
{
    scanf("%d%d", &n, &m);
    alen = 0;
    memset(last, 0, sizeof(last)); //注意构图之前一定要初始化,不然后果很严重!
    for (int i = 1; i <= m; i++)
    {
        int x, y, c;
        scanf("%d%d%d", &x, &y, &c); //题目给出的是无向边,而我们的边目录是有向边
        ins(x, y, c);
        ins(y, x, c); //建立正向边、反向边
    }
    spfa();
    return 0;
}

Comentários detalhados são escritos no código, então olhe para ele.

exemplo 

Não deixe passar, vá até Luogu para verificar você mesmo.

 Diz-se que um comportamento que merece uma surra precisa de uma foto para confortá-lo.

Acho que você gosta

Origin blog.csdn.net/aliyonghang/article/details/131152177
Recomendado
Clasificación