Algoritmo de plantilla-dijkstra de ruta más corta

algoritmo dijkstra

El camino más corto de una sola fuente de un gráfico ponderado dirigido es adecuado para encontrar el camino más corto desde todos los puntos hasta un cierto vértice i.
Ámbito de aplicación : gráficos donde todos los pesos de los bordes son números positivos. Y permitir la existencia de bucles.
Dividido en dos tipos de algoritmo dijkstra ingenuo y algoritmo dijkstra de optimización de montón .

La idea del algoritmo dijkstra:
dividir los vértices en dos categorías: se han contado los vértices del camino más cortoColeccionesY no se han contado los vértices del camino más corto.
Usando la idea codiciosa, cada vez que el vértice j más cercano a i nunca se encuentra en los vértices del conjunto s, se agrega as, y luego se actualiza la distancia desde el punto que se puede alcanzar desde j hasta el vértice i, tomando el valor mínimo , y así repitiendo n veces, se agregan N vértices al conjunto s.

La diferencia algorítmica entre el algoritmo ingenuo y el algoritmo de optimización del montón es que el vértice j más cercano a i nunca se encuentra entre los vértices del conjunto s.

El algoritmo ingenuo es adecuado para gráficos densos, utilizando almacenamiento de matriz de adyacencia , y la complejidad de tiempo es O (n 2 )

g[i][j]=x, Lo que significa que la distancia i-> j es x

El algoritmo de optimización del montón es adecuado para gráficos dispersos, utilizando el almacenamiento de la tabla de adyacencia , y la complejidad de tiempo es O (mlogn)

n número de puntos m número de lados

Análisis : la influencia del lado pesado y el bucle propio en el algoritmo dijkstra

Lazo.
Self-loop: la situación de uno mismo a otro; y el ciclo en
sí definitivamente no se actualizará a sí mismo, sino que solo lo actualizarán otros, porque cuando actualiza la distancia a otros puntos, ya se ha unido a los conjuntos y ya se han contado. El punto de la distancia más corta no es válido, no importa si está actualizado o no.
Un bucle es un caso general de un bucle propio. Si hay un bucle en el gráfico, de acuerdo con la idea de Dijkstra, no pasará por el bucle. Cuando el último nodo del bucle debe pasar por el bucle, significa que el nodo anterior ha sido agregado al conjunto s., ya no estará en la categoría considerada, y no actualizará la distancia, porque solo se considerarán los casos que no estén en el conjunto s.

Los bordes gruesos son fáciles de manejar. Cuando hay varios bordes entre dos puntos, simplemente tome el borde más corto.

Descripción del Título

Dado un gráfico dirigido con n puntos y m aristas, puede haber múltiples aristas y auto-bucles en el gráfico, y todos los pesos de los bordes son positivos.

Encuentre la distancia más corta del punto 1 al punto N. Si no puede caminar desde el punto 1 al punto n, salida -1.

Formato de entrada La
primera línea contiene los números enteros ny m.

Cada una de las siguientes m filas contiene tres enteros x, y, z, lo que indica que hay un borde dirigido desde el punto x al punto y, y la longitud del lado es z.

Formato de
salida Genera un número entero, lo que significaLa distancia más corta del punto 1 al punto n

Si la ruta no existe, se emite -1.

Muestra de entrada:

3 3
1 2 2
2 3 1
1 3 4

Salida de muestra:

3

Algoritmo dijkstra ingenuo

El rango de datos es
1≤n≤500,
1≤m≤10 5 ,
y la longitud del lado involucrado en la figura no excede 10,000.

Análisis:
n vértices, a lo sumo n × (n-1) gráficos dirigidos) <no considere aristas pesadas y auto-bucles> , n 2 es igual a 2.5e5, m es igual a 1e5, myn 2 son uno level, que es un gráfico denso Adopte el almacenamiento de matriz de adyacencia.

#include <iostream>
#include <cstring>

using namespace std;

const int N=510,INF=0x3f3f3f3f;
int g[N][N];
int dis[N];
bool vis[N]; //判断该结点是否已加入统计完最短路径的集合s,初始时为false,均未加入
int n,m;//n个点 m条边

void dijkstra(int s)//处理 1号结点->所有结点 的最短路径
{
    
    
    memset(dis,0x3f,sizeof dis);
    dis[s]=0;  //s到自身距离为0
    //最多循环n次,每次选出一个距离1号结点最近的结点,可求出每个结点到s结点的距离
    //第一次选出的一定是s结点自己
    int num=n;
    while (num--) {
    
    
        int t=-1; //最终选出的t号结点,先初始化为小于1的结点(因为正常的结点是从1号开始)
        for (int i=1;i<=n;i++) {
    
    
            if (!vis[i] && (t==-1 || dis[i]<dis[t])) t=i;
        }
        
        if (t==n) break; 
        这题特定的结束条件:我们只需求1->n结点的最短路径。
        此时到了n号结点,但是仍不能确定dis[t]的值是不是无穷大,设想一下:n个顶点,0条边的情况。
        
        vis[t]=true;
        //接下来更新 s->其它结点 通过t结点的最短距离
        for (int i=1;i<=n;i++) {
    
    
            if (!vis[i]) {
    
     只需更新不在集合中的结点,但是if条件可省略
                dis[i]=min(dis[i],dis[t]+g[t][i]);
            }  若i是已经记录过最短路径的点,因为t后于i加入集合,所以dis[i]<=dis[t]必然成立(贪心),所以这里不需要!vis[j]的判断
        }
    }
}

int main()
{
    
    
    scanf("%d%d",&n,&m);
    //初始化邻接矩阵,规定自身到自身距离为0,但是求最短路时自环的边无效,可以不用初始化。
    memset(g,0x3f,sizeof g);
    //存储边
    int a,b,w;
    while (m--) {
    
    
        scanf("%d%d%d",&a,&b,&w);
        g[a][b]=min(g[a][b],w);//处理重边的情况,取最小值的边,同时忽略自环
    }
    dijkstra(1); //求1号点到所有点的最短距离。
    dis[n]==INF?cout<<"-1":cout<<dis[n]; //输出1号顶点到n号顶点的最短距离
    
    return 0;
}

La matriz dis almacena la suma de las rutas más cortas desde el nodo s hasta todos los nodos, se basa en la idea de la codicia y selecciona un nodo que está más cerca del nodo s cada vez.
Por lo tanto, en el ciclo while (num–) num, el valor de la matriz dis [] seleccionada cada vez aumenta a su vez.

Entonces, en la línea 31 a la línea 34, el if (!vis[i]) { dis[i]=min(dis[i],dis[t]+g[t][i]);}juicio si se puede agregar o no.
Porque si el nodo i es un nodo que se ha agregado al conjunto s, entonces debe haber dis [i] <= dis [t], y los pesos de los bordes en el gráfico son todos números positivos, entonces dis [i ] no se verá afectado.

Algoritmo dijkstra de optimización de montón

El algoritmo de optimización del montón es optimizar las 21 22 23 líneas anteriores, utilizando el montón más pequeño, y buscar directamente los vértices con la distancia más pequeña cada vez, en lugar de pasar por n ciclos para determinar.

La información que debe registrarse en el montón se ordena por nodo y distancia, y luego se ordena por distancia. Por tanto pair<int,int>, el primero almacena la distancia y el segundo almacena el número de vértice. Luego de sacarlo es necesario actualizar la distancia, luego de la distancia actualizada se debe modificar el dis array, pero hay otro problema : si el nodo ya existe en el heap, implicará la modificación del heap nuevamente. Dos palabras clave. Modificación del montón El montón del contenedor stl no se puede realizar, aunque se puede realizar escribiendo a mano el montón, es más complicado.

Por lo tanto, no lo modificamos y lo volvemos a insertar en el montón. Este enfoque es que no necesitamos escribir el montón, pero los elementos del montón son redundantes. Puede haber varios elementos con el mismo vértice pero diferentes distancias en El montón original Puede haber como máximo N elementos (porque hay como máximo N vértices), y puede haber más ahora, pero esto no tiene ningún efecto. Cada vértice sólo actualizará la distancia una vez de acuerdo con su distancia más corta. Puede juzgar si el vértice se basa en la matriz vis. No está ya agregado al conjunto de rutas más cortas conocidas para evitar la duplicación.

Otro punto a tener en cuenta: en el
enfoque ingenuo, la distancia más corta desde el nodo i hasta todos los demás nodos se puede determinar haciendo un bucle como máximo n veces.
La teoría de la optimización del montón debería ser la misma, pero reemplazamos la operación de modificación con la operación de inserción, por lo que eso significa que un nodo puede repetirse varias veces, aunque solo la distancia más corta será efectiva, y luego se filtrará a través de la vis matriz Si ​​hay un vértice INF inalcanzable, se colocará detrás de todos los vértices.
y entonces,Hay dos condiciones de terminación para la optimización del montón: la cola no está vacía || El número de conteos verdaderos en la matriz vis es menor que n

Rango de datos:
1≤n, m≤1.5 × 10 5 , la
longitud del lado en la figura no es menor que 0 ni mayor que 10,000.

myn son un nivel, es un gráfico disperso, que usa almacenamiento de tabla de adyacencia, optimización de montón.

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

typedef pair<int,int> PII; //first表示距离,second表示节点编号,按距离升序排列,每次选出最小的
priority_queue< PII,vector<PII>,greater<PII> >heap;
const int N=2e5,INF=0x3f3f3f3f;
int h[N],e[N],w[N],ne[N],idx;
int dis[N];
bool vis[N];
int n,m;

void add(int a,int b,int c)
{
    
    
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

void dijkstra(int s)
{
    
    
    memset(dis,0x3f,sizeof dis);
    dis[s]=0;
    
    heap.push({
    
    dis[s],s});
    int num=n;   n在后面还要用到,不能修改,因此用num替代
    while(heap.size() && num) {
    
     //当队列不空时 或者 成功计数小于n次
        PII t=heap.top();
        heap.pop();
        //每次取出距离s最近的结点,然后开始更新距离
        int v=t.second,d=t.first;   //s->v的最短距离为d
        if (vis[v]) continue; //已经更新过,重复更新的情况跳过,否则
        vis[v]=true,num--;//还剩下n个点未统计
        for (int i=h[v];i!=-1;i=ne[i]) {
    
    
            int j=e[i];
            if (!vis[j] && dis[j]>d+w[i]) {
    
     //同理,if条件可省略
                dis[j]=d+w[i];
                heap.push({
    
    dis[j],j});
            }
        }
    }
}

int main()
{
    
    
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    int a,b,c;
    while (m--) {
    
    
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    dijkstra(1);
    dis[n]==INF?puts("-1"):printf("%d",dis[n]);
    
    return 0;
}

En la línea 25, solo el punto de inicio s se coloca en el montón, y los vértices restantes deben colocarse y luego actualizarse, pero el montón en stl no se puede actualizar por palabra clave, sino insertar En lugar de actualizar, los puntos restantes no se actualizarán se instalará y se instalará cuando llegue la actualización.

En cuanto al juicio while en la línea 27, existen dos condiciones de juicio: while(heap.size() && num)
esto se debe a la aparición de elementos redundantes, lo que imposibilita finalizar n ciclos. Es posible que los vértices del montón en los n ciclos sean los puntos donde se ha calculado la distancia más corta. Este ciclo no es válido, por lo que solo funciona después de n ciclos válidos . Al mismo tiempo, la condición heap.size()es evitar que el montón sea nulo antes de n ciclos válidos. Imagine una situación en la que un gráfico no conectado : n vértices, con 0 aristas, el montón está vacío durante el segundo ciclo, pero aún necesita realizar un ciclo, PII t=heap.top();habrá un problema y se producirá TLE.

Si se trata de un montón escrito a mano que se puede actualizar, entonces basta con repetir n veces y todos los puntos se pueden poner en el montón al principio.

Supongo que te gusta

Origin blog.csdn.net/HangHug_L/article/details/113784559
Recomendado
Clasificación