Network Delay Time
Sebuah jaringan memiliki n simpul yang diberi label dari 1 hingga n. Tautan times[i] = [u, v, w] membawa sinyal dari simpul u ke simpul v dalam waktu w satuan, hanya dalam arah tersebut.
Sebuah sinyal meninggalkan simpul k pada waktu 0 dan merambat melalui setiap tautan yang dapat dilaluinya. Kembalikan waktu saat simpul terakhir menerimanya, atau -1 jika ada simpul yang tidak pernah menerimanya.
Fungsi
- timesinteger-2d-array
- tautan berarah, masing-masing sebagai [u, v, w]
- ninteger
- jumlah node
- kinteger
- node yang mengirimkan sinyal
- Mengembalikaninteger
- waktu saat node terakhir menerima sinyal, atau -1
Batasan
2 ≤ n ≤ 30001 ≤ times.length ≤ 4000times[i].length = 31 ≤ u, v ≤ ndanu ≠ v0 ≤ w ≤ 100- Tidak ada dua tautan yang memiliki
udanvyang sama. 1 ≤ k ≤ n
Contoh
- Input
- times = [[1, 2, 4], [1, 3, 1], [3, 2, 2], [2, 4, 1]]n = 4k = 1
- Output
- 4
- Penjelasan
- Tautan langsung mencapai simpul 2 pada waktu 4, tetapi rute melalui simpul 3 tiba pada waktu 1 + 2 = 3. Simpul 4 menerima sinyal paling akhir, pada waktu 3 + 1 = 4.
- Input
- times = [[1, 2, 3], [3, 1, 2]]n = 3k = 1
- Output
- -1
- Penjelasan
- Node 2 menerima sinyal pada waktu 3, tetapi satu-satunya tautan yang terhubung ke node 3 berjalan dari 3 ke 1. Node 3 tidak pernah menerima sinyal itu, jadi jawabannya adalah -1.
- Input
- times = [[2, 1, 5], [2, 3, 0], [3, 1, 2]]n = 3k = 2
- Output
- 2
- Penjelasan
- Link ke node 3 bernilai 0, jadi node 3 menerima sinyal pada waktu 0. Node 1 kemudian menerimanya pada waktu 0 + 2 = 2, lebih cepat daripada 5 dari link langsungnya.
Pertanyaan lanjutan
Misalkan sinyal melemah setelah melewati m tautan. Bagaimana cara menemukan waktu node terakhir mendengarnya dalam batas tersebut?
Petunjuk
Buka satu per satu. Setiap petunjuk mengungkap sedikit lebih banyak.
Sebuah node menerima sinyal setelah waktu tempuh rute tercepat menuju node tersebut dari
k. Jadi, jawabannya adalah waktu tercepat terbesar di antara semua node, atau -1 jika ada node yang tidak memiliki rute sama sekali.Tidak ada tautan yang memerlukan waktu negatif. Jadi, di antara simpul yang waktunya belum final, simpul dengan waktu sementara terkecil tidak mungkin dicapai lebih cepat: setiap rute lain menuju simpul itu melewati simpul lain yang waktunya juga belum final, dan simpul tersebut dicapai tidak lebih cepat. Tetapkan simpul sesuai urutan kedatangannya.
Simpan min-heap berisi pasangan
(time, node). Keluarkan nilai terkecil, lewati jika node tersebut sudah memiliki waktu yang lebih kecil, lalu masukkan setiap tetangga yang waktunya membaik. Saat heap kosong, ambil waktu terbesar.
Solusi
Setiap node menerima sinyal dalam waktu yang sama dengan panjang rute tercepat dari k, jadi ini adalah pencarian jalur terpendek dari satu sumber yang diikuti dengan nilai maksimum, atau -1 jika sebuah node tidak memiliki rute. Waktu tempuh link tidak pernah negatif, jadi algoritma Dijkstra menetapkan setiap node satu kali. Di bawah ini, E adalah times.length.
Penelusuran mendalam yang mengunjungi kembali setiap kali menemukan rute yang lebih cepat
Benar, tetapi tidak selesai pada tes terbesar
Intuisi
Mulai pencarian secara depth-first dari k, sambil membawa waktu yang telah berlalu, lalu catat waktu kedatangan tercepat untuk setiap node. Rute yang mencapai suatu node tidak lebih cepat daripada catatannya akan berhenti, sedangkan rute yang lebih cepat akan memperbarui catatan dan melanjutkan pencarian. Setiap catatan akhirnya menunjukkan waktu tercepat yang sebenarnya, karena rute tercepat akan memperbaruinya pada suatu titik.
Satu node dapat diperbarui berkali-kali. Susun 18 gerbang dalam satu baris, setiap pasang gerbang dihubungkan oleh tautan gratis dan jalan memutar dengan waktu tempuh 65,536, 32,768, dan seterusnya hingga 1. Jika mencoba jalan memutar terlebih dahulu, pencarian mencapai gerbang terakhir pada 131,072 waktu yang berbeda dan menelusuri semua bagian setelahnya setiap kali: pekerjaan yang bertumbuh secara eksponensial terhadap n.
Algoritma
- Atur setiap waktu terbaik ke infinity, lalu dorong
(k, 0)ke sebuah stack. - Keluarkan
(node, t). Lewati jikat < best[node]tidak terpenuhi, lalu aturbest[node] = t. - Dorong
(next, t + w)untuk setiap tautan darinodeyang waktu tibanya lebih baik daripadabest[next]. - Saat stack kosong, kembalikan -1 jika ada waktu terbaik yang bernilai tak hingga, jika tidak, kembalikan nilai terbesar.
def networkDelayTime(times, n, k):
graph = [[] for _ in range(n + 1)]
for u, v, w in times:
graph[u].append((v, w))
INF = float("inf")
best = [INF] * (n + 1) # the fastest arrival found so far at each node
stack = [(k, 0)]
while stack:
node, t = stack.pop()
# A route that is not faster than one already found adds nothing.
if t >= best[node]:
continue
best[node] = t
# Push in reverse so the first listed edge is explored first.
for nxt, w in reversed(graph[node]):
if t + w < best[nxt]:
stack.append((nxt, t + w))
worst = 0
for node in range(1, n + 1):
if best[node] == INF:
return -1
worst = max(worst, best[node])
return worstBellman-Ford: relaksasikan setiap sisi hingga n-1 kali
Intuisi
Simpan waktu sementara dist untuk setiap node, dengan nilai 0 untuk k. Relaksasi sisi u → v menurunkan dist[v] menjadi dist[u] + w jika nilainya lebih kecil. Bellman-Ford merelaksasi setiap sisi pada setiap putaran.
Ambil rute tercepat ke sebuah node, misalnya k → a → b → v. Putaran pertama menetapkan nilai final dist[a], putaran kedua menetapkan dist[b], dan putaran ketiga menetapkan dist[v]. Rute tercepat tidak pernah mengulangi node, jadi rute itu memiliki paling banyak n-1 sisi, dan n-1 putaran menetapkan nilai untuk setiap node.
Setiap putaran membutuhkan biaya O(E). Rantai 3.000 node yang dicantumkan dari ujung terjauhnya ke belakang membutuhkan 2.999 putaran untuk memeriksa 4.000 sisi: sekitar 12 juta operasi relaksasi.
Algoritma
- Tetapkan
dist[k] = 0dan semuadistlainnya ke tak terhingga. - Ulangi hingga n-1 kali: untuk setiap tautan
[u, v, w], jikadist[u] + w < dist[v], tetapkandist[v] = dist[u] + w. - Berhenti lebih awal setelah satu putaran yang tidak mengubah apa pun.
- Kembalikan -1 jika ada
distyang masih tak terhingga, jika tidak, kembalikan nilai terbesar.
def networkDelayTime(times, n, k):
INF = float("inf")
dist = [INF] * (n + 1)
dist[k] = 0
# A fastest route visits each node at most once, so it has at most n-1 edges,
# and n-1 passes over every edge are enough to find it.
for _ in range(n - 1):
changed = False
for u, v, w in times:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
changed = True
if not changed: # a pass that improves nothing means every time is final
break
worst = 0
for node in range(1, n + 1):
if dist[node] == INF:
return -1
worst = max(worst, dist[node])
return worstAlgoritma Dijkstra dengan min-heap
Intuisi
Di antara node yang belum ditetapkan, node dengan waktu sementara terkecil sudah menjadi final. Rute lain ke node itu harus meninggalkan node yang sudah ditetapkan melalui node yang belum ditetapkan dengan waktu setidaknya sebesar itu, dan jalur setelahnya hanya menambah waktu karena tidak ada yang negatif. Jadi, tetapkan node itu, relaksasikan jalurnya sekali, lalu ulangi.
Min-heap menemukan waktu terkecil. Saat nilai node membaik, masukkan entri baru dan biarkan entri lama tetap di tempatnya. Entri yang dikeluarkan dengan waktu di atas dist sudah usang, jadi lewati entri itu. Setiap jalur memasukkan paling banyak satu entri, jadi heap menampung paling banyak E + 1 entri dan totalnya adalah O(E log E).
Algoritma
- Atur
dist[k] = 0, setiapdistlainnya ke tak hingga, lalu masukkan(0, k)ke min-heap. - Keluarkan
(t, node)terkecil; jikat > dist[node], lewati karena sudah kedaluwarsa. - Untuk setiap tautan
node → nextdengan waktuw, jikat + w < dist[next], aturdist[next] = t + wlalu masukkan(t + w, next). - Saat heap kosong, kembalikan -1 jika ada
distyang tak hingga, jika tidak, kembalikan nilai terbesar.
import heapq
def networkDelayTime(times, n, k):
graph = [[] for _ in range(n + 1)]
for u, v, w in times:
graph[u].append((v, w))
INF = float("inf")
dist = [INF] * (n + 1)
dist[k] = 0
heap = [(0, k)] # (arrival time, node), smallest time on top
while heap:
t, node = heapq.heappop(heap)
# A stale entry: this node was already reached sooner.
if t > dist[node]:
continue
# t is now final: every other route reaches node later.
for nxt, w in graph[node]:
if t + w < dist[nxt]:
dist[nxt] = t + w
heapq.heappush(heap, (t + w, nxt))
worst = 0
for node in range(1, n + 1):
if dist[node] == INF:
return -1
worst = max(worst, dist[node])
return worst
Jebakan dan kasus tepi
- Menganggap tautan berjalan dua arah.
[3, 1, 2]hanya membawa sinyal dari 3 ke 1, jadi node 3 tidak pernah mendengarnya dalam contoh kedua. - Menggunakan pencarian melebar biasa. Algoritma ini menemukan rute dengan tautan paling sedikit: dalam contoh pertama, algoritma ini memberi node 2 waktu 4, bukan 3.
- Melupakan -1. Mengambil nilai maksimum tanpa pemeriksaan akan menghasilkan tak terhingga, atau menyembunyikan node yang tidak pernah mendengar sinyal.
- Menggunakan int terbesar sebagai tak terhingga.
dist[u] + wkemudian meluap menjadi angka negatif untukuyang belum tercapai; lewati node yang belum tercapai atau gunakan 10^9.
Pertanyaan yang sering diajukan3
Berapa kompleksitas waktu Network Delay Time?
Dengan algoritma Dijkstra dan binary heap, kompleksitas waktunya adalah O(E log E), dengan E sebagai jumlah tautan. Nilai itu sama dengan O(E log n), karena E paling banyak n². Kompleksitas ruangnya adalah O(n + E). Bellman-Ford membutuhkan waktu O(n · E).
Mengapa algoritma Dijkstra membutuhkan bobot yang tidak negatif?
Dijkstra tidak pernah mengunjungi kembali node yang sudah ditetapkan, yang aman hanya jika tidak ada rute berikutnya yang lebih singkat. Dengan jalur A → B berdurasi 2, A → C berdurasi 3, dan C → B berdurasi -2, algoritma ini menetapkan B pada 2, tetapi rute melalui C mencapainya pada 1. Bellman-Ford menangani durasi negatif.
Bisakah kamu menyelesaikan Network Delay Time tanpa heap?
Ya. Dijkstra dengan array biasa memindai setiap simpul yang belum ditetapkan untuk mencari waktu terkecil, yang secara keseluruhan membutuhkan biaya O(n²). Cara ini lebih unggul pada graf padat, saat E mendekati n². Pada graf jarang, seperti graf dengan 3,000 simpul dan 4,000 sisi, versi heap melakukan jauh lebih sedikit pekerjaan.
Soal serupa
Soal yang memakai ide yang sama. Mengerjakan dua atau tiga di antaranya membuat polanya melekat.
def networkDelayTime(times, n, k):
# Tulis kodemu di siniTes #1
Tes #2
Tes #3
Argumen
times = [[1, 2, 4], [1, 3, 1], [3, 2, 2], [2, 4, 1]] n = 4 k = 1
Output yang diharapkan
4