Diámetro de un Árbol: Técnicas de Programación Dinámica y BFS

El diámetro de un árbol es la lnogitud del camino más largo entre dos nodos. Existen enfoques eficientes para calcularlo, como la programación dinámica en árboles y el algoritmo de doble búsqueda en amplitud (BFS).

Programación Dinámica en Árboles

Puede manejar aristas con pesos negativos.

Complejidad temporal: O(n).

Sea distancia[x] la máxima distancia alcanzable desde el nodo x en el subárbol que lo tiene como raíz. Para cada nodo hijo y_i, con peso de arista peso(x, y_i), se cumple:

distancia[x] = max(distancia[y_i] + peso(x, y_i)) para todo i.

Para calcular la cadena más larga que pasa por x, se combina la mayer y segunda mayor distancia hacia sus hijos:

cadena_larga[x] = max(distancia[y_i] + distancia[y_j] + peso(x, y_i) + peso(x, y_j)) donde j ≠ i.

struct Arista {int destino, peso;};
vector<vector>> grafo;
int diametro_global = 0;
vector<bool> visitado;
vector<int> max_dist;

void dfs(int nodo) {
    visitado[nodo] = true;
    for (const auto& arista : grafo[nodo]) {
        if (visitado[arista.destino]) continue;
        dfs(arista.destino);
        diametro_global = max(diametro_global, max_dist[nodo] + max_dist[arista.destino] + arista.peso);
        max_dist[nodo] = max(max_dist[nodo], max_dist[arista.destino] + arista.peso);
    }
}
</int></bool></vector>

Doble BFS para Diámetro

No soporta pesos negativos en las aristas.

Complejidad temporal: O(n).

Este método no solo calcula la longitud del diámetro, sino que también puede recuperar los nodos que lo forman.

struct Nodo {int id, dist_recorrido;};
struct Arista {int destino, peso;};

vector<vector>> grafo;
vector<bool> visitado;
vector<int> padre;

Nodo bfs_lejano(int inicio) {
    queue<Nodo> cola;
    visitado.assign(grafo.size(), false);
    cola.push({inicio, 0});
    visitado[inicio] = true;
    Nodo mas_lejano = {inicio, 0};

    while (!cola.empty()) {
        Nodo actual = cola.front(); cola.pop();
        if (actual.dist_recorrido > mas_lejano.dist_recorrido) {
            mas_lejano = actual;
        }
        for (const auto& arista : grafo[actual.id]) {
            if (!visitado[arista.destino]) {
                visitado[arista.destino] = true;
                cola.push({arista.destino, actual.dist_recorrido + arista.peso});
                padre[arista.destino] = actual.id;
            }
        }
    }
    return mas_lejano;
}

pair<int, vector<int>> obtener_diametro() {
    Nodo primero = bfs_lejano(0);
    Nodo segundo = bfs_lejano(primero.id);
    vector<int> camino;
    camino.push_back(segundo.id);

    int actual = segundo.id;
    while (actual != primero.id) {
        actual = padre[actual];
        camino.push_back(actual);
    }
    reverse(camino.begin(), camino.end());
    return {segundo.dist_recorrido, camino};
}

int main() {
    int num_nodos;
    cin >> num_nodos;
    grafo.resize(num_nodos);
    padre.resize(num_nodos, -1);

    for (int i = 0; i < num_nodos - 1; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        u--; v--;
        grafo[u].push_back({v, w});
        grafo[v].push_back({u, w});
    }

    auto [longitud, camino] = obtener_diametro();
    cout << longitud << endl;
    for (int id : camino) {
        cout << id + 1 << " ";
    }
    cout << endl;
    return 0;
}
</int></bool></vector>

Etiquetas: grafos programación dinámica BFS C++ algoritmos en árboles

Publicado el 6-24 07:06