beecrowd 1148 — entendendo cada caso de entrada.
O exemplo oficial possui dois casos de teste antes do 0 0. Vamos tratar cada um como um exercício independente para não misturar arestas, consultas e respostas.
Como a entrada é organizada?
N E: quantidade de cidades e quantidade de acordos.
As próximas E linhas são arestas X Y H.
Depois vem K: quantidade de consultas.
As próximas K linhas são pares origem destino.
Quando aparecer 0 0, a entrada terminou.
4 5 e o segundo começa em 3 3.
Caso de teste 1 — entrada completa
Entrada somente do caso 1
4 5 1 2 5 2 1 10 3 4 8 4 3 7 2 3 6 5 1 2 1 3 1 4 4 3 4 1
Saída somente do caso 1
0 6 6 0 Nao e possivel entregar a carta
Interpretação linha por linha
Caso 1 — do grafo original ao grafo usado
Antes da regra especial
1 → 2 = 5 2 → 1 = 10 2 → 3 = 6 3 → 4 = 8 4 → 3 = 7
Depois da regra especial
1 → 2 = 0 2 → 1 = 0 2 → 3 = 6 3 → 4 = 0 4 → 3 = 0
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | 0 | ∞ | ∞ |
| 2 | 0 | 0 | 6 | ∞ |
| 3 | ∞ | ∞ | 0 | 0 |
| 4 | ∞ | ∞ | 0 | 0 |
Caso 1 — resolvendo todas as 5 consultas
O par 1 e 2 possui comunicação instantânea após a regra especial.
Chegamos em 2 sem custo e depois seguimos pela aresta 2→3 de custo 6.
Depois de chegar em 3, a comunicação 3→4 custa zero.
O par 3 e 4 também foi transformado em comunicação instantânea.
4 chega em 3, mas a aresta entre 2 e 3 existe apenas no sentido 2→3. Portanto não conseguimos voltar para 2 e depois para 1.
Caso de teste 2 — entrada completa
Entrada somente do caso 2
3 3 1 2 10 2 3 1 3 2 1 3 1 3 3 1 3 2
Saída somente do caso 2
10 Nao e possivel entregar a carta 0
Interpretação linha por linha
Caso 2 — montando o grafo completo
Antes da regra
1 → 2 = 10 2 → 3 = 1 3 → 2 = 1
Depois da regra
1 → 2 = 10 2 → 3 = 0 3 → 2 = 0
| 1 | 2 | 3 | |
|---|---|---|---|
| 1 | 0 | 10 | ∞ |
| 2 | ∞ | 0 | 0 |
| 3 | ∞ | 0 | 0 |
Caso 2 — resolvendo todas as 3 consultas
1→2 ainda custa 10. Depois 2→3 custa zero.
3 chega em 2 por custo zero, mas não existe uma aresta de 2 para 1.
2 e 3 possuem acordos recíprocos, então o envio é instantâneo.
Agora fica claro onde o Dijkstra entra
Nos exemplos pequenos conseguimos enxergar os caminhos. O programa precisa fazer a mesma coisa automaticamente.
Monte o grafo lendo as E linhas.
Aplique a regra de custo zero quando existir ligação direta nos dois sentidos.
Para cada consulta, inicialize dist[origem] = 0 e as demais como infinito.
Escolha sempre o vértice não visitado com a menor distância acumulada.
Relaxe as arestas: tente melhorar o custo dos vizinhos.
Se o destino continuar infinito, imprima Nao e possivel entregar a carta.
Exemplo de Dijkstra: Caso 1, consulta 1 → 4
| Passo | Escolhido | dist[1] | dist[2] | dist[3] | dist[4] | Atualização |
|---|---|---|---|---|---|---|
| Inicial | — | 0 | ∞ | ∞ | ∞ | Origem = 0 |
| 1 | 1 | 0 | 0 | ∞ | ∞ | 1→2 custa 0 |
| 2 | 2 | 0 | 0 | 6 | ∞ | 0 + 6 = 6 |
| 3 | 3 | 0 | 0 | 6 | 6 | 6 + 0 = 6 |
| 4 | 4 | 0 | 0 | 6 | 6 | Resposta = 6 |
Código completo e comentado em Python 3.11
# Um custo muito alto representa uma ligação inexistente. INF = 10 ** 15 def dijkstra(origem, destino, n): # dist guarda o menor custo conhecido desde a origem. # visitado indica as cidades cujo menor custo já foi confirmado. dist = [INF] * (n + 1) visitado = [False] * (n + 1) dist[origem] = 0 for _ in range(n): menor = INF atual = -1 # Escolhe o não visitado com menor distância. for cidade in range(1, n + 1): if not visitado[cidade] and dist[cidade] < menor: menor = dist[cidade] atual = cidade # Nenhuma outra cidade alcançável restou. if atual == -1: break visitado[atual] = True # Ao confirmar o destino, sua distância já é mínima. if atual == destino: break # Relaxamento. for vizinho in range(1, n + 1): peso = grafo[atual][vizinho] if peso != INF: novo_custo = dist[atual] + peso # Uma rota mais barata substitui a estimativa anterior. if novo_custo < dist[vizinho]: dist[vizinho] = novo_custo return dist[destino] while True: # Cada caso começa pela quantidade de cidades e acordos. n, e = map(int, input().split()) # O par 0 0 encerra toda a entrada. if n == 0 and e == 0: break # A matriz guarda o custo direto; INF significa ausência de acordo. grafo = [ [INF] * (n + 1) for _ in range(n + 1) ] for cidade in range(1, n + 1): grafo[cidade][cidade] = 0 for _ in range(e): x, y, h = map(int, input().split()) # Em entradas repetidas, preservamos o menor custo direto. grafo[x][y] = min(grafo[x][y], h) # Se o acordo inverso já existe, os dois custos viram zero. if grafo[y][x] != INF: grafo[x][y] = 0 grafo[y][x] = 0 # Cada consulta executa Dijkstra entre uma origem e um destino. k = int(input()) for _ in range(k): origem, destino = map(int, input().split()) resposta = dijkstra(origem, destino, n) # INF ao final significa que nenhuma rota alcançou o destino. if resposta == INF: print("Nao e possivel entregar a carta") else: print(resposta) # O formato exige uma linha vazia depois de cada caso. print()
Entrada oficial completa, agora separada mentalmente
CASO 1 4 5 1 2 5 2 1 10 3 4 8 4 3 7 2 3 6 5 1 2 1 3 1 4 4 3 4 1 CASO 2 3 3 1 2 10 2 3 1 3 2 1 3 1 3 3 1 3 2 FIM 0 0