Solução para o Problema POJ 3268: Determinando a Maior Distância de Ida e Volta em Grafos Direcionados

No problema POJ 3268, existe um ponto de encontro designado como x onde ocorre uma reunião. Todos os outros pontos no grafo precisam viajar até x e, em seguida, retornar ao ponto de partida original. O objetivo é calcular a distância total máxima percorrida em um ciclo completo de ida e volta.

Para resolver isso, precisamos encontrar os caminhos mais curtos de cada nó para x e de x para cadda nó. Devido à natureza direcionada do grafo, algoritmos de caminho mínimo como Dijkstra e SPFA são adequados.

Abordagem com Algoritmo de Dijkstra

Esta solução executa o algoritmo de Dijkstra duas vezes: uma para calcular as distâncias mínimas de todos os nós até x (caminhos inversos) e outra para as distâncias mínimas de x até todos os nós (caminhos diretos). A implemetnação abaixo utiliza uma matriz de adjacência para representar o grafo.


#include <iostream>
#include <cstdio>
#include <cstring>
#include <climits>

using namespace std;

const int MAXIMO_NOS = 1010;
const int SEM_CAMINHO = INT_MAX;

int matrizAdj[MAXIMO_NOS][MAXIMO_NOS];
int totalNos, totalArestas, pontoCentral;
int distParaX[MAXIMO_NOS], distDeX[MAXIMO_NOS];
bool processado[MAXIMO_NOS];

void prepararMatriz() {
    for (int i = 1; i <= totalNos; ++i) {
        for (int j = 1; j <= totalNos; ++j) {
            if (i == j) {
                matrizAdj[i][j] = 0;
            } else {
                matrizAdj[i][j] = SEM_CAMINHO;
            }
        }
    }
}

void dijkstraParaX() {
    for (int i = 1; i <= totalNos; ++i) {
        distParaX[i] = matrizAdj[i][pontoCentral];
    }
    memset(processado, false, sizeof(processado));
    processado[pontoCentral] = true;

    for (int iteracao = 0; iteracao < totalNos; ++iteracao) {
        int menor = SEM_CAMINHO;
        int noSelecionado = -1;
        for (int no = 1; no <= totalNos; ++no) {
            if (!processado[no] && distParaX[no] < menor) {
                menor = distParaX[no];
                noSelecionado = no;
            }
        }
        if (noSelecionado == -1) break;
        processado[noSelecionado] = true;
        for (int vizinho = 1; vizinho <= totalNos; ++vizinho) {
            if (!processado[vizinho] && distParaX[vizinho] > matrizAdj[vizinho][noSelecionado] + distParaX[noSelecionado]) {
                distParaX[vizinho] = matrizAdj[vizinho][noSelecionado] + distParaX[noSelecionado];
            }
        }
    }
}

void dijkstraDeX() {
    for (int i = 1; i <= totalNos; ++i) {
        distDeX[i] = matrizAdj[pontoCentral][i];
    }
    memset(processado, false, sizeof(processado));
    processado[pontoCentral] = true;

    for (int iteracao = 0; iteracao < totalNos; ++iteracao) {
        int menor = SEM_CAMINHO;
        int noSelecionado = -1;
        for (int no = 1; no <= totalNos; ++no) {
            if (!processado[no] && distDeX[no] < menor) {
                menor = distDeX[no];
                noSelecionado = no;
            }
        }
        if (noSelecionado == -1) break;
        processado[noSelecionado] = true;
        for (int vizinho = 1; vizinho <= totalNos; ++vizinho) {
            if (!processado[vizinho] && distDeX[vizinho] > distDeX[noSelecionado] + matrizAdj[noSelecionado][vizinho]) {
                distDeX[vizinho] = distDeX[noSelecionado] + matrizAdj[noSelecionado][vizinho];
            }
        }
    }
}

int main() {
    while (scanf("%d %d %d", &totalNos, &totalArestas, &pontoCentral) == 3) {
        prepararMatriz();
        int origem, destino, custo;
        for (int i = 0; i < totalArestas; ++i) {
            scanf("%d %d %d", &origem, &destino, &custo);
            if (custo < matrizAdj[origem][destino]) {
                matrizAdj[origem][destino] = custo;
            }
        }
        dijkstraParaX();
        dijkstraDeX();

        int maxDistanciaTotal = -1;
        for (int i = 1; i <= totalNos; ++i) {
            if (distParaX[i] != SEM_CAMINHO && distDeX[i] != SEM_CAMINHO) {
                int distanciaCiclo = distParaX[i] + distDeX[i];
                if (distanciaCiclo > maxDistanciaTotal) {
                    maxDistanciaTotal = distanciaCiclo;
                }
            }
        }
        printf("%d\n", maxDistanciaTotal);
    }
    return 0;
}

Abordagem com Algoritmo SPFA e Grafo Invertido

Uma alternativa eficiente é usar o algoritmo SPFA. Para obter as distâncias de volta, construímos um grafo invertido revertando as direções das arestas. Assim, executamos o SPFA no grafo original para distâncias de ida e no grafo invertido para distâncias de volta. Esta implementação emprega listas de adjacência para melhor desempenho.


#include <iostream>
#include <cstdio>
#include <cstring>
#include <climits>
#include <queue>
#include <vector>

using namespace std;

const int LIMITE_NOS = 101000;
const int INFINITO = INT_MAX;

int qtdNos, qtdArestas, noEncontro;
vector<pair<int, int>> rede[LIMITE_NOS];
vector<pair<int, int>> redeInvertida[LIMITE_NOS];
int distIda[LIMITE_NOS], distVolta[LIMITE_NOS];
bool naFila[LIMITE_NOS];

void spfaCalcularIda() {
    for (int i = 1; i <= qtdNos; ++i) {
        distIda[i] = INFINITO;
    }
    distIda[noEncontro] = 0;
    memset(naFila, false, sizeof(naFila));
    queue<int> fila;
    fila.push(noEncontro);
    naFila[noEncontro] = true;

    while (!fila.empty()) {
        int noCorrente = fila.front();
        fila.pop();
        naFila[noCorrente] = false;
        for (const auto& aresta : rede[noCorrente]) {
            int vizinho = aresta.first;
            int peso = aresta.second;
            if (distIda[vizinho] > distIda[noCorrente] + peso) {
                distIda[vizinho] = distIda[noCorrente] + peso;
                if (!naFila[vizinho]) {
                    naFila[vizinho] = true;
                    fila.push(vizinho);
                }
            }
        }
    }
}

void spfaCalcularVolta() {
    for (int i = 1; i <= qtdNos; ++i) {
        distVolta[i] = INFINITO;
    }
    distVolta[noEncontro] = 0;
    memset(naFila, false, sizeof(naFila));
    queue<int> fila;
    fila.push(noEncontro);
    naFila[noEncontro] = true;

    while (!fila.empty()) {
        int noCorrente = fila.front();
        fila.pop();
        naFila[noCorrente] = false;
        for (const auto& aresta : redeInvertida[noCorrente]) {
            int vizinho = aresta.first;
            int peso = aresta.second;
            if (distVolta[vizinho] > distVolta[noCorrente] + peso) {
                distVolta[vizinho] = distVolta[noCorrente] + peso;
                if (!naFila[vizinho]) {
                    naFila[vizinho] = true;
                    fila.push(vizinho);
                }
            }
        }
    }
}

int main() {
    int u, v, w;
    while (scanf("%d %d %d", &qtdNos, &qtdArestas, &noEncontro) == 3) {
        for (int i = 1; i <= qtdNos; ++i) {
            rede[i].clear();
            redeInvertida[i].clear();
        }
        for (int i = 0; i < qtdArestas; ++i) {
            scanf("%d %d %d", &u, &v, &w);
            rede[u].emplace_back(v, w);
            redeInvertida[v].emplace_back(u, w);
        }
        spfaCalcularIda();
        spfaCalcularVolta();

        int maxDistancia = -1;
        for (int i = 1; i <= qtdNos; ++i) {
            if (distIda[i] != INFINITO && distVolta[i] != INFINITO) {
                int totalDistancia = distIda[i] + distVolta[i];
                if (totalDistancia > maxDistancia) {
                    maxDistancia = totalDistancia;
                }
            }
        }
        printf("%d\n", maxDistancia);
    }
    return 0;
}

Tags: dijkstra spfa caminho_mais_curto grafos_direcionados c_plus_plus

Publicado em 6-6 18:59 por Thomas