Tilbake
6.2

6.2 Korteste vei — BFS, Dijkstra, DAG og reversert graf

Korteste-vei-verktøykassen — BFS (uvektet), Dijkstra (ikke-negative vekter), DAG via topsort, Bellman-Ford (fakta) — og det avgjørende reversert-Dijkstra-trikset.

60 min
7 oppgaver
Korteste veiBFSDijkstraDAGreversert graf
Din fremgang i kapitlet
0 / 7 oppgaver

Sist du var her — forkunnskaper i kortform

Tre resultater fra tidligere kapitler brukes hele veien her. De står ferdig
oppfrisket, så du slipper å bla:

1. Bredde-først-søk (BFS) besøker nodene lagvis. Fra startnoden tas først alle
naboer, så alle naboers naboer, og så videre. Kjøretiden er
O(V+E)O(|V| + |E|): hver node tas ut av køen én gang, hver kant behandles én gang.
Detaljene står i kap. 5.2.

2. Min-heapen tar ut det minste elementet i O(logn)O(\log n). Den er et array med
indeks fra 0; forelder til ii er (i1)/2\lfloor (i-1)/2 \rfloor, og barna er 2i+12i+1 og
2i+22i+2. RemoveMin koster O(logn)O(\log n), og det samme gjør Insert og
DecreaseKey. Se kap. 4.4. Dette er prioritetskøen Dijkstra
bruker, og hele Dijkstras kjøretid kommer derfra.

3. Topologisk sortering ordner en DAG slik at alle kanter peker framover, i
O(V+E)O(|V| + |E|) med Kahns algoritme. Se kap. 6.1. Det er
grunnen til at en vektet DAG kan løses raskere enn en generell graf.

Trenger du å friske opp hva en naboliste er, står det i
kap. 5.1.

Notasjons- og pseudokodeliste

Løkke 1 — når alle skritt koster like mye (ca. 10 min)

Et gangveinett mellom åtte bygg. Alle strekkene tar omtrent like lang tid å gå, så
det eneste som teller, er hvor mange strekk du må innom. Hvor mange strekk er
det fra bygg AA til bygg HH?

Når alle kanter koster det samme, er korteste vei det samme som færrest kanter,
og da trenger du ingen prioritetskø. Bredde-først-søket tar nodene i den
rekkefølgen de ligger fra startnoden: først alle som er ett skritt unna, så alle
som er to skritt unna. Første gang du når en node, er det via den korteste veien —
det ligger i selve lagvis-rekkefølgen.

📜BFS gir korteste vei i uvektet graf

I en graf uten vekter (eller der alle vekter er like), gir bredde-først-søk fra
ss korteste avstand fra ss til alle andre noder, målt i antall kanter.

Hvorfor: BFS tar nodene ut av køen i ikke-synkende avstandsrekkefølge. Alle
noder med avstand 1 tas ut før noen med avstand 2, og så videre. Første gang en
node oppdages, skjer det derfor fra en node på laget rett foran — og det er en
korteste vei.

Kjøretid: O(V+E)O(|V| + |E|). Hver node legges i køen og tas ut nøyaktig én gang,
og hver kant ses på nøyaktig én gang (to ganger i en urettet graf, én gang fra hver
ende — det endrer ikke orden).

Merk hva som gjør dette galt for vektede grafer: BFS teller kanter. En vei med
to dyre kanter er kortere i antall kanter enn en vei med tre billige, men kan
være mye lengre i kostnad. Å bruke BFS på en vektet graf er en av de faste
fellene på matrisen.

✏️Eksempel 1: BFS i gangveinettet

Gangveinettet har strekkene AABB, AACC, BBDD, CCDD, CCEE, DDFF,
EEGG, FFHH og GGHH — alle like lange og alle toveis. Finn antall strekk
fra AA til hver av de andre byggene.

Køen behandles i alfabetisk rekkefølge blant like kandidater, slik at sporingen
kan gjenskapes nøyaktig.

StegTatt ut av køenNye noder oppdagetKø etteravstand-tabell etter
1A (avst. 0)B = 1, C = 1B, CA=0, B=1, C=1, D=∞, E=∞, F=∞, G=∞, H=∞
2B (avst. 1)D = 2C, DA=0, B=1, C=1, D=2, E=∞, F=∞, G=∞, H=∞
3C (avst. 1)E = 2D, EA=0, B=1, C=1, D=2, E=2, F=∞, G=∞, H=∞
4D (avst. 2)F = 3E, FA=0, B=1, C=1, D=2, E=2, F=3, G=∞, H=∞
5E (avst. 2)G = 3F, GA=0, B=1, C=1, D=2, E=2, F=3, G=3, H=∞
6F (avst. 3)H = 4G, HA=0, B=1, C=1, D=2, E=2, F=3, G=3, H=4
7G (avst. 3)ingenHA=0, B=1, C=1, D=2, E=2, F=3, G=3, H=4
8H (avst. 4)ingentomA=0, B=1, C=1, D=2, E=2, F=3, G=3, H=4

Sluttilstand — dette er svaret du leverer:
A=0, B=1, C=1, D=2, E=2, F=3, G=3, H=4
Se på steg 6 og 7. Node HH oppdages fra FF med avstand 4. Senere tas GG ut
med avstand 3, og GG har også en kant til HH — men HH er allerede oppdaget, og
en ny vei via GG ville gitt 3+1=43 + 1 = 4, altså det samme. BFS ser ikke engang på
den muligheten, og trenger ikke: første gang en node oppdages, er avstanden
allerede minimal.
Kontrollen: avstandene skal stige langs køen. Ser du at en node tas ut med
lavere avstand enn den forrige, har du gjort en feil i sporingen.

Fellenote. Fella her er å bruke denne metoden på en graf med vekter. Hadde

strekket AACC tatt ti minutter og AABBDD tatt tre, ville BFS fortsatt

sagt at CC er «nærmere» enn DD. BFS teller kanter, ikke minutter.

📝Oppgave 1

(Innstegsoppgave, sjanger H — grafalgoritme, altså at du gjenkjenner problemet og
bruker riktig algoritme.) Et uvektet nett har kantene AABB, AADD, BBCC,
CCEE, DDEE, EEFF, FFGG, alle toveis.

a) Kjør BFS fra AA og oppgi avstanden til hver node.
b) Hvilken algoritme ville du brukt hvis kantene hadde hatt ulike, positive
vekter?

Løkke 2 — Dijkstra: alltid den nærmeste uferdige (ca. 18 min)

Nå koster strekkene forskjellig. Et bilnett mellom seks knutepunkter, med
kjøretider i minutter og enveiskjøring på alle strekk. Hvor lang tid tar det fra
AA til hvert av de andre?

Grunnideen i Dijkstras algoritme er én setning: ta alltid den uferdige noden med
lavest kjent avstand, og erklær den ferdig.
Når du gjør det, kan ingen senere
oppdagelse gjøre den kortere — for enhver annen vei dit måtte gått gjennom en node
med høyere avstand, og alle kanter koster minst 0.

Den siste halvsetningen er hele forutsetningen: alle kanter må være
ikke-negative.
Vi kommer tilbake til hva som skjer når de ikke er det.

Relaksering
Grunnoperasjonen i alle korteste-vei-algoritmer: for en kant fra uu til vv,
sjekk om veien via uu er bedre enn det du har.

avstand[v]=min(avstand[v], avstand[u]+w(u,v))\text{avstand}[v] = \min\bigl(\text{avstand}[v],\ \text{avstand}[u] + w(u, v)\bigr)

Blir avstanden lavere, oppdateres den, og forgjenger[v] settes til uu. Kostnad
O(1)O(1) per kant. De fire korteste-vei-algoritmene i dette kapitlet gjør nøyaktig
det samme — de skiller seg bare i rekkefølgen kantene relakseres i.

Dijkstras algoritme

Finner korteste vei fra én kilde til alle noder i en graf med ikke-negative
kantvekter, ved hjelp av en prioritetskø.

Kjøretid O((V+E)logV)O((|V| + |E|)\log |V|) med binær heap. Krever ikke-negative vekter;
gir gale svar hvis en kant er negativ, selv om grafen ikke har negativ sykel.

📜Pseudokode-kontrakt: `Dijkstra`
Antagelser om representasjon. Grafen G=(V,E)G = (V, E) er gitt som nabolister, der
G.naboer(v) gir parene (w, vekt) for hver kant v -> w. Alle vekter er
ikke-negative.
avstand og forgjenger er arrayer indeksert på node.
Prioritetskøen PQ er en binær min-heap ordnet på avstand, med RemoveMin og
DecreaseKey i O(logV)O(\log |V|) — se kap. 4.4.

Prebetingelse: ingen kantvekt er negativ.
Postbetingelse: avstand[v] er korteste avstand fra s til v for alle v,
og \infty for noder som ikke kan nås.

Procedure Dijkstra(G, s)
  Input:  vektet graf G = (V, E) som nabolister, alle vekter >= 0; startnode s
  Output: avstand[v] = korteste avstand fra s til v, for alle v i V
  for hver v i V:
      avstand[v] = uendelig
      forgjenger[v] = ingen
  avstand[s] = 0
  PQ = min-heap med alle noder i V, ordnet paa avstand
  while PQ er ikke tom:
      u = PQ.RemoveMin()
      for hver (v, vekt) i G.naboer(u):
          if v er fortsatt i PQ and avstand[u] + vekt < avstand[v]:
              avstand[v] = avstand[u] + vekt
              forgjenger[v] = u
              PQ.DecreaseKey(v, avstand[v])
  return avstand, forgjenger

Invarianten i én setning: når en node tas ut av prioritetskøen, er avstand
til den noden endelig — ingen senere relaksering kan senke den, fordi enhver annen
vei dit må gå via en node som fortsatt ligger i køen og derfor har minst like høy
avstand.

Kjøretid: O((V+E)logV)O((|V| + |E|)\log |V|). Tell operasjonene: V|V| kall på
RemoveMin, hvert O(logV)O(\log |V|), gir O(VlogV)O(|V| \log |V|). for-løkka kjøres én
gang per node, og går over den nodens naboliste — til sammen E|E| ganger — og
hver relaksering kan utløse en DecreaseKey à O(logV)O(\log |V|), altså
O(ElogV)O(|E| \log |V|). Summen er O((V+E)logV)O((|V| + |E|)\log |V|).

Merk at det er en sum, ikke et produkt. Å skrive O(VElogV)O(|V| \cdot |E| \log |V|)
er samme telleefeil som ved topologisk sortering: den indre løkka går over nabolista
til den ene noden, ikke over alle kanter.

✏️Eksempel 2: Dijkstra i bilnettet

Kjøretidene i minutter, alle enveiskjørte: A -> B 4, A -> C 2, C -> B 1,
B -> D 5, C -> D 8, C -> E 10, D -> E 2, D -> F 6, E -> F 3.

Finn korteste kjøretid fra AA til alle knutepunktene.

Prioritetskøen inneholder alle noder fra starten, med \infty som avstand for
alle utenom AA. Kolonnen lengst til høyre viser køens innhold som heap-array
med indeks fra 0 — altså slik heapen faktisk lagrer det, ikke sortert. Element 0 er
alltid det minste; resten står i heap-orden.

StegFerdig (tatt ut av køen)Relakserte kanteravstand-tabell etterPrioritetskø etter (heap-array)
1A (avstand 0)B: ∞ -> 4, C: ∞ -> 2A=0, B=4, C=2, D=∞, E=∞, F=∞(2, C), (∞, F), (4, B), (∞, D), (∞, E)
2C (avstand 2)B: 4 -> 3, D: ∞ -> 10, E: ∞ -> 12A=0, B=3, C=2, D=10, E=12, F=∞(3, B), (10, D), (12, E), (∞, F)
3B (avstand 3)D: 10 -> 8A=0, B=3, C=2, D=8, E=12, F=∞(8, D), (∞, F), (12, E)
4D (avstand 8)E: 12 -> 10, F: ∞ -> 14A=0, B=3, C=2, D=8, E=10, F=14(10, E), (14, F)
5E (avstand 10)F: 14 -> 13A=0, B=3, C=2, D=8, E=10, F=13(13, F)
6F (avstand 13)ingenA=0, B=3, C=2, D=8, E=10, F=13tom

Sluttilstand — dette er svaret du leverer:
A=0, B=3, C=2, D=8, E=10, F=13
Se på steg 2. Kanten A -> B ga først avstand 4 til BB. Men CC tas ut før
BB, og fra CC er det bare 1 minutt til BB: 2+1=3<42 + 1 = 3 < 4. Avstanden til BB
senkes, og forgjenger[B] blir CC, ikke AA. Den direkte kanten er ikke
korteste vei.
Det er nettopp derfor du trenger algoritmen og ikke bare kan lese
av grafen.

Se på steg 3. Nå tas BB ut med avstand 3. Fra dette øyeblikket er 3 endelig —

og det er trygt, fordi alt annet i køen har avstand minst 3, og ingen kant kan

trekke fra.
Korteste vei til FF, lest ut av forgjengerne: FF kom fra EE, EE fra DD,
DD fra BB, BB fra CC, CC fra AA. Veien er
A -> C -> B -> D -> E -> F med kostnad 2+1+5+2+3=132 + 1 + 5 + 2 + 3 = 13. Stemmer med
tabellen.

Kontrollen som fanger de fleste feil: avstandene på nodene som tas ut, skal
aldri synke. Her er de 0, 2, 3, 8, 10, 13 — stigende hele veien. Synker den, har du
enten regnet feil eller så har grafen en negativ kant.

Fellenote. Den vanligste feilen her er å avslutte for tidlig — å stoppe når
målnoden er oppdaget i stedet for når den er tatt ut av køen. I steg 2 er FF
fortsatt \infty, i steg 4 er den 14, og først i steg 5 blir den 13. Sensor ber om
avstandstabellen ved terminering.

📝Oppgave 2
Sjanger E

En rettet vektet graf har kantene A -> B 7, A -> C 3,
C -> B 2, B -> D 4, C -> D 9, B -> E 6, D -> E 1.

a) Kjør Dijkstra fra AA og oppgi hele avstandstabellen.
b) Hvilken vei går korteste rute til EE?
c) Hvorfor er avstanden til BB ikke 7?

Løkke 3 — når en kant kan være negativ (ca. 16 min)

Negative kanter høres kunstig ut i et veinett, men de er helt vanlige så snart
vekten er noe annet enn avstand: en transaksjon som gir gevinst, et
produksjonstrinn som frigjør kapasitet, et bytte som gir tilbake mer enn det
kostet.

Og de ødelegger Dijkstra. Ikke litt — helt.

✏️Eksempel 3: Dijkstra gir feil svar på én negativ kant

En graf har kantene A -> B 5, A -> C 6, C -> B −4 og B -> D 1. Grafen har
ingen sykel i det hele tatt, og altså heller ingen negativ sykel.

Kjør Dijkstra, kjør deretter Bellman-Ford, og forklar forskjellen.

Dijkstra først:

StegFerdig (tatt ut av køen)Relakserte kanteravstand-tabell etterPrioritetskø etter (heap-array)
1A (avstand 0)B: ∞ -> 5, C: ∞ -> 6A=0, B=5, C=6, D=∞(5, B), (∞, D), (6, C)
2B (avstand 5)D: ∞ -> 6A=0, B=5, C=6, D=6(6, C), (6, D)
3C (avstand 6)ingenA=0, B=5, C=6, D=6(6, D)
4D (avstand 6)ingenA=0, B=5, C=6, D=6tom

Dijkstra svarer A=0, B=5, C=6, D=6.
Bellman-Ford på samme graf. Algoritmen relakserer alle kanter, gjentatte
ganger, i V1|V| - 1 runder:
RundeKanter som ga forbedringavstand-tabell etter
1A -> B: ∞ -> 5, A -> C: ∞ -> 6, B -> D: ∞ -> 6, C -> B: 5 -> 2A=0, B=2, C=6, D=6
2B -> D: 6 -> 3A=0, B=2, C=6, D=3
3ingen endringA=0, B=2, C=6, D=3

Bellman-Ford svarer A=0, B=2, C=6, D=3.

Sluttilstand — det riktige svaret:

A=0, B=2, C=6, D=3
Hvor Dijkstra gikk galt. I steg 2 ble BB tatt ut med avstand 5 og erklært

ferdig. Men veien A -> C -> B koster 6+(4)=26 + (-4) = 2, altså mindre. Da CC ble tatt
ut i steg 3, var BB allerede ute av køen, og relakseringen ble aldri gjort.
Feilen forplantet seg videre til DD: 6 i stedet for 3.
Hvorfor invarianten brøt sammen. Dijkstras garanti hviler på at ingen kant kan
trekke fra: enhver vei via noder som fortsatt ligger i køen, må koste minst like

mye som den noden du tar ut. Med en negativ kant er det ikke sant lenger — en dyr

node kan lede til en billig kant.

Merk at grafen ikke engang har en sykel. Det er en utbredt misforståelse at

Dijkstra bare feiler ved negative sykler. Én enkelt negativ kant holder.
Fellenote. Fella er å bruke Dijkstra fordi grafen «ser grei ut». Kontrollen
tar to sekunder: finnes det en negativ vekt i det hele tatt? Hvis ja, er
Dijkstra utelukket — da er svaret Bellman-Ford, eller topologisk sortering hvis

grafen er en DAG.

Bellman-Ford

Finner korteste vei fra én kilde selv når negative kantvekter finnes, så lenge
grafen ikke har en negativ sykel.

Metoden er enkel: relakser alle kanter, og gjenta V1|V| - 1 ganger. Etter
runde kk er alle korteste veier med høyst kk kanter funnet, og en korteste vei
kan aldri ha mer enn V1|V| - 1 kanter.

Kjøretid O(VE)O(|V| \cdot |E|) — klart tregere enn Dijkstra, og det er prisen for å
tåle negative vekter. En ekstra runde som fortsatt gir forbedringer, avslører at
grafen har en negativ sykel.

Negativ sykel

En sykel der summen av kantvektene er negativ. Da finnes det ingen korteste
vei: hver ny runde rundt sykelen gjør veien billigere, i det uendelige.

Bellman-Ford oppdager det ved å kjøre én runde ekstra: gir runde nummer V|V|
fortsatt forbedringer, finnes en negativ sykel. I IN2010 er dette et faktapunkt,
ikke en oppgavetype.

Men det finnes en sak til, og den er den viktigste av dem alle på matrisen:
hva om grafen er en DAG?

I en DAG kan nodene ordnes slik at alle kanter peker framover. Går du gjennom
nodene i den rekkefølgen og relakserer utkantene deres, er hver node ferdig i
det du kommer til den — alt som kunne forbedret den, ligger bak deg. Da trenger du
ingen prioritetskø, og det spiller ingen rolle om noen vekter er negative.

📜Pseudokode-kontrakt: `DAGKortesteVei`
Antagelser om representasjon. G=(V,E)G = (V, E) er en rettet asyklisk vektet
graf gitt som nabolister. Vektene kan være negative. avstand er et array
indeksert på node.

Prebetingelse: GG er en DAG. Postbetingelse: avstand[v] er korteste
avstand fra s til alle v som kan nås, og \infty for resten.

Procedure DAGKortesteVei(G, s)
  Input:  vektet DAG G = (V, E) som nabolister, startnode s
  Output: avstand[v] = korteste avstand fra s til v
  topo = TopologiskSortering(G)
  for hver v i V:
      avstand[v] = uendelig
  avstand[s] = 0
  for hver u i topo, i rekkefolge:
      if avstand[u] er uendelig:
          fortsett med neste u
      for hver (v, vekt) i G.naboer(u):
          if avstand[u] + vekt < avstand[v]:
              avstand[v] = avstand[u] + vekt
              forgjenger[v] = u
  return avstand

Invarianten i én setning: når u behandles i topologisk rekkefølge, er alle
noder med en kant inn til u allerede behandlet, så avstand[u] kan ikke bli
lavere senere.

Kjøretid: O(V+E)O(|V| + |E|). Den topologiske sorteringen koster
O(V+E)O(|V| + |E|) (se kap. 6.1), og løkka etterpå går gjennom
hver node én gang og hver kant én gang. To lineære pass etter hverandre er
fortsatt lineært.

Dette er raskere enn DijkstraO(V+E)O(|V| + |E|) mot
O((V+E)logV)O((|V| + |E|)\log |V|) — og det tåler i tillegg negative vekter. Prisen er
forutsetningen: grafen må være asyklisk.

✏️Eksempel 4: Korteste vei i en vektet DAG med en negativ kant

Seks produksjonstrinn med kantene A -> B 3, A -> C 7, B -> C 2, B -> D 6,
C -> D 1, C -> E −3, D -> E 2, D -> F 5, E -> F 8. Kanten C -> E er
negativ fordi trinnet frigjør lagerplass.

Finn laveste totalkostnad fra AA til hvert trinn.

Steg 1 — topologisk rekkefølge. Alle kanter går fra en tidligere til en senere
bokstav, så grafen er en DAG, og A, B, C, D, E, F er en lovlig
topologisk rekkefølge.

Steg 2 — én gjennomgang i den rekkefølgen, med relaksering:

StegNode i topologisk rekkefølgeRelakserte kanteravstand-tabell etter
1AB: ∞ -> 3, C: ∞ -> 7A=0, B=3, C=7, D=∞, E=∞, F=∞
2BC: 7 -> 5, D: ∞ -> 9A=0, B=3, C=5, D=9, E=∞, F=∞
3CD: 9 -> 6, E: ∞ -> 2A=0, B=3, C=5, D=6, E=2, F=∞
4DF: ∞ -> 11A=0, B=3, C=5, D=6, E=2, F=11
5EF: 11 -> 10A=0, B=3, C=5, D=6, E=2, F=10
6FingenA=0, B=3, C=5, D=6, E=2, F=10

Sluttilstand — dette er svaret du leverer:
A=0, B=3, C=5, D=6, E=2, F=10
Se på EE. Avstanden til EE er 2, som er lavere enn avstanden til både
CC (5) og DD (6) — noe som er umulig i en graf med bare ikke-negative vekter.
Det er den negative kanten C -> E som gjør det: 5+(3)=25 + (-3) = 2.
Og se hva Dijkstra ville gjort her: den ville tatt ut EE … nei, den ville
først tatt ut AA, så BB (3), så CC (5), og deretter DD (6) — men i det CC ble

tatt ut, ble EE satt til 2, altså lavere enn CC selv. En avstand som er lavere

enn den noden vi nettopp erklærte ferdig, bryter Dijkstras hele forutsetning. På

denne grafen hadde vi vært heldige og fått riktig svar likevel, men garantien er
borte, og på grafen i eksempel 3 var svaret feil. Du kan ikke bruke «det gikk
bra sist» som argument.

Kontrollen: hver node skal ha fått sin endelige verdi før den behandles i
rekkefølgen. Det følger av topologien, og det er derfor én gjennomgang holder.
Poengtrapp-notat. På en oppgave der grafen er en DAG, er
O(V+E)O(|V| + |E|) det øverste trinnet. Dijkstra (O((V+E)logV)O((|V| + |E|)\log |V|)) er
korrekt hvis alle vekter er ikke-negative, men tregere, og gir færre poeng.
Bellman-Ford (O(VE)O(|V| \cdot |E|)) er riktigst av alle i den forstand at den
alltid virker — og gir minst poeng, fordi den er den tregeste.

📝Oppgave 3
Sjanger H

En vektet DAG har kantene A -> B 4, A -> C 2, C -> B −1,
B -> D 3, C -> D 8, D -> E 2.

a) Oppgi en topologisk rekkefølge.
b) Finn korteste avstand fra AA til alle nodene, ved å gå gjennom i den
rekkefølgen.
c) Hvorfor kan du ikke bruke Dijkstra her, og hvorfor spiller det ingen rolle
at grafen ikke har noen sykel?

📜Korteste-avstander-matrisen — de fire radene

Dette er det tryggeste Del 1-poenget i Del 6, og det kommer som en avkryssing
(sjanger F). Lær radene som en enhet:

Grafen er …BrukKjøretid
uvektetbredde-først-søk (BFS)O(V+E)O(\lvert V \rvert + \lvert E \rvert)
vektet DAG (asyklisk)topologisk sortering, så én gjennomgangO(V+E)O(\lvert V \rvert + \lvert E \rvert)
vektet uten negative kanterDijkstra med prioritetskøO((V+E)logV)O((\lvert V \rvert + \lvert E \rvert)\log \lvert V \rvert)
vektet med negative kanter, ingen negativ sykelBellman-FordO(VE)O(\lvert V \rvert \cdot \lvert E \rvert)

Slik leser du matrisen riktig. Radene er ordnet fra raskest til tregest, og du
skal alltid velge den øverste raden som passer. En vektet DAG uten negative
kanter kan løses med Dijkstra — men topologisk sortering er raskere, og
poengtrappen belønner det raskeste korrekte svaret.
De to spørsmålene som avgjør raden:

1. Har kantene vekter i det hele tatt? Nei: bruk BFS.
2. Er grafen asyklisk? Ja: bruk topologisk sortering. Nei: finnes det en
negativ kant? Nei: bruk Dijkstra. Ja: bruk Bellman-Ford.
Merk at «ingen negativ sykel» er kravet til Bellman-Ford, ikke til Dijkstra.

Dijkstra krever at ingen kant er negativ. Å blande disse to er en fast felle,

og den koster hele avkryssingen.

📝Oppgave 4
Sjanger F

Fyll
inn den raskeste korrekte algoritmen og dens kjøretid for hver graftype.

a) Et uvektet sosialt nettverk.
b) En vektet, asyklisk arbeidsflyt der noen kanter har negativ vekt.
c) Et veinett med kjøretider i minutter, alle positive, med rundkjøringer
(altså sykler).
d) Et handelsnett med gevinster og tap på hver overføring, sykler finnes, men
ingen sykel gir netto gevinst.

Løkke 4 — trikset som skiller B fra A: snu grafen (ca. 14 min)

Nå kommer det grepet som oftest avgjør toppkarakteren på en korteste-vei-oppgave.

Situasjonen er alltid den samme i formen: du har flere mulige utgangspunkt og
ett mål, og spørsmålet er hvilket utgangspunkt som er nærmest målet. En
brannstasjon som skal velges, en av flere lagre som skal levere, en av flere
inngangsdører som skal brukes.

Den nærliggende løsningen er å kjøre Dijkstra fra hvert utgangspunkt og
sammenligne. Med U|U| utgangspunkt koster det U|U| Dijkstra-kjøringer, altså
O(U(V+E)logV)O(|U| \cdot (|V| + |E|)\log |V|).

Den gode løsningen snur grafen. Avstanden fra pp til tt i GG er nøyaktig
det samme som avstanden fra tt til pp i den reverserte grafen GRG^R. Så kjør
én Dijkstra, fra tt, i GRG^R — og les av avstanden til alle utgangspunktene
på én gang.

✏️Eksempel 5: Eksamensnivå — hvilket utgangspunkt er nærmest?

Tre beredskapspunkter PP, QQ og RR skal vurderes. Et oppdrag skal utføres på
sted TT. Veinettet er enveiskjørt med kjøretider: P -> S 4, Q -> S 2,
Q -> M 7, R -> M 3, S -> T 5, M -> T 1.

Hvilket beredskapspunkt kommer raskest fram til TT? Skriv en algoritme som løser
problemet generelt, med lavest mulig kjøretid.

Problemet navngitt. Dette er korteste vei til én bestemt node fra flere
mulige startnoder. Nøkkelobservasjonen er at «avstand fra pp til tt i GG» er det
samme som «avstand fra tt til pp i GRG^R» — den samme veien, lest baklengs.

Antagelser om representasjon. G=(V,E)G = (V, E) er en rettet vektet graf gitt som
nabolister, med ikke-negative kjøretider. UVU \subseteq V er mengden av
mulige utgangspunkt, og tt er målnoden. Vi kan bygge den reverserte grafen GRG^R
ved én gjennomgang av alle nabolistene, O(V+E)O(|V| + |E|).

Algoritmen.

Procedure NaermesteUtgangspunkt(G, U, t)
  Input:  rettet vektet graf G = (V, E), alle vekter >= 0;
          mengden U av mulige utgangspunkt; maalnoden t
  Output: den noden i U som har lavest kjoeretid til t, og selve kjoeretiden
  G_R = G med alle kanter snudd
  avstand = Dijkstra(G_R, t)
  beste = ingen
  for hver p i U:
      if beste er ingen or avstand[p] < avstand[beste]:
          beste = p
  return beste, avstand[beste]

Kjøring på tallene. Den reverserte grafen har kantene T -> S 5, T -> M 1,
S -> P 4, S -> Q 2, M -> Q 7, M -> R 3. Dijkstra fra TT:

StegFerdig (tatt ut av køen)Relakserte kanteravstand-tabell etterPrioritetskø etter (heap-array)
1T (avstand 0)M: ∞ -> 1, S: ∞ -> 5M=1, P=∞, Q=∞, R=∞, S=5, T=0(1, M), (5, S), (∞, Q), (∞, R), (∞, P)
2M (avstand 1)Q: ∞ -> 8, R: ∞ -> 4M=1, P=∞, Q=8, R=4, S=5, T=0(4, R), (5, S), (8, Q), (∞, P)
3R (avstand 4)ingenM=1, P=∞, Q=8, R=4, S=5, T=0(5, S), (∞, P), (8, Q)
4S (avstand 5)P: ∞ -> 9, Q: 8 -> 7M=1, P=9, Q=7, R=4, S=5, T=0(7, Q), (9, P)
5Q (avstand 7)ingenM=1, P=9, Q=7, R=4, S=5, T=0(9, P)
6P (avstand 9)ingenM=1, P=9, Q=7, R=4, S=5, T=0tom

Sluttilstand:
M=1, P=9, Q=7, R=4, S=5, T=0
Svaret: RR er nærmest, med 4 minutter. Deretter QQ med 7 og PP med 9.
Kjøretid: O((V+E)logV)O((|V| + |E|)\log |V|) — én reversering (O(V+E)O(|V| + |E|)), én
Dijkstra, og én gjennomgang av UU (O(U)O(|U|), som er høyst O(V)O(|V|)). Her er V|V|
antall veikryss og E|E| antall veistrekk.
Hvorfor dette er lavest mulig, og hva den naive løsningen koster. Kjører du

Dijkstra fra hvert utgangspunkt, blir det U|U| kjøringer:

O(U(V+E)logV)O(|U| \cdot (|V| + |E|)\log |V|). Med tre beredskapspunkter er det tre ganger

så mye arbeid; med hundre er det hundre ganger. Reverseringen fjerner faktoren

U|U| helt.
Poengtrappen på denne oppgavetypen er dokumentert eksplisitt i arkivet: den
reverserte løsningen er toppsvaret, U|U| Dijkstra-kjøringer er et korrekt

midtsvar, og det er nøyaktig den forskjellen som skiller A fra B. Felle #1
igjen: full gjennomgang der ett grep holdt.
Alternativet du også kan nevne: legg til en kunstig supernode ss^* med
kanter av vekt 0 inn til alle noder i UU, og kjør én Dijkstra fra ss^*. Det gir
korteste avstand fra «noe i UU» til tt i én kjøring — men det gir deg ikke

hvilket utgangspunkt som var best, uten å lese av forgjengerne. Reverseringen er
det reneste svaret her.

📝Oppgave 5
Sjanger H

I den reverserte grafen fra eksempel 5 fikk vi
M=1, P=9, Q=7, R=4, S=5, T=0.

a) Hvor lang tid tar det fra QQ til TT i den opprinnelige grafen, og
hvilken vei går ruten?
b) Et nytt beredskapspunkt NN legges til, med kanten N -> M av vekt 2. Blir
NN det nærmeste?
c) Hvorfor kan du svare på b) uten å kjøre Dijkstra på nytt fra bunnen?

📝Oppgave 6
Sjanger H, krevende

En leverandør har lagre i nodemengden UU og skal levere til
alle kunder i nodemengden KK. For hver kunde vil de vite hvilket lager som er
nærmest.

a) Hva koster det å kjøre reversert Dijkstra én gang per kunde?
b) Hva koster det å kjøre Dijkstra én gang per lager?
c) Hvilken av de to velger du, og hva er betingelsen?
d) Skriv løsningen for det tilfellet at U|U| er mye mindre enn K|K|.

📝Oppgave 7
Sjanger C

Marker sant eller usant, og begrunn hvert svar med én setning.

a) Dijkstra gir riktig svar så lenge grafen ikke har en negativ sykel.
b) Bellman-Ford kan brukes på enhver graf der Dijkstra kan brukes.
c) BFS gir korteste vei i en vektet graf der alle kanter har vekt 1.
d) Topologisk sortering kan brukes til korteste vei i en graf med sykler.
e) Dijkstra med binær prioritetskø er O(VElogV)O(|V| \cdot |E| \log |V|).

Begrepsbank

Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har
lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder
kjernestoffet.

Kortene her er kalibrert som eksamen tester dem: algoritme, kjøretid,
forutsetning.
Fordi eksamen er hjelpemiddelfri, er dette puggematerialet.

Korteste-avstander-matrisen

De fire faste radene, ordnet fra raskest til tregest:

- uvektet: BFS, O(V+E)O(|V| + |E|)
- vektet DAG: topologisk sortering og én gjennomgang, O(V+E)O(|V| + |E|)
- ingen negative kanter: Dijkstra, O((V+E)logV)O((|V| + |E|)\log |V|)
- negative kanter, ingen negativ sykel: Bellman-Ford, O(VE)O(|V| \cdot |E|)

Velg alltid den øverste raden som passer.

Dijkstras forutsetning
Ingen kantvekt får være negativ. Ikke «ingen negativ sykel» — det er
Bellman-Fords krav.

Grunnen ligger i invarianten: når en node tas ut av køen, er avstanden endelig
fordi enhver annen vei dit må gå via en node med minst like høy avstand. En
negativ kant kan trekke fra, og da faller argumentet.

Bellman-Fords kjøretid og forutsetning
O(VE)O(|V| \cdot |E|): alle E|E| kanter relakseres i hver av V1|V| - 1 runder.

Forutsetningen er at grafen ikke har en negativ sykel. Negative enkeltkanter er
helt greit — det er hele grunnen til at algoritmen finnes. Én ekstra runde som
fortsatt gir forbedringer, avslører en negativ sykel.

Hvorfor V1|V| - 1 runder i Bellman-Ford

En korteste vei kan ikke ha mer enn V1|V| - 1 kanter — hadde den hatt flere, måtte
den besøkt en node to ganger, altså gått i en sykel, og en sykel uten negativ
totalvekt kan alltid kuttes ut.

Etter runde kk er alle korteste veier med høyst kk kanter funnet. Etter
V1|V| - 1 runder er derfor alt funnet.

Korteste vei i vektet DAG

Sortér topologisk, gå gjennom nodene i den rekkefølgen, og relakser utkantene til
hver node. O(V+E)O(|V| + |E|).

Metoden er raskere enn Dijkstra og tåler i tillegg negative kantvekter. Den
eneste forutsetningen er at grafen er asyklisk. Når oppgaven sier «arbeidsflyt»,
«produksjonskjede» eller «avhengigheter», er det ofte denne raden i matrisen som
gjelder.

Reversert Dijkstra

Flere mulige startnoder, ett mål: snu alle kanter og kjør én Dijkstra fra
målet. Avstanden fra pp til tt i GG er den samme som fra tt til pp i GRG^R.

Kjøretid O((V+E)logV)O((|V| + |E|)\log |V|) mot O(U(V+E)logV)O(|U| \cdot (|V| + |E|)\log |V|) for én
kjøring per utgangspunkt. Dette er det dokumenterte A/B-skillet på
korteste-vei-oppgaver.

Supernode-trikset

Alternativ til reversering når du bare vil vite den korteste avstanden fra
noen node i en mengde UU: legg til en kunstig node ss^* med kanter av vekt 0
til alle noder i UU, og kjør én Dijkstra fra ss^*.

Kostnaden er O(U)O(|U|) ekstra kanter og endrer ikke kjøretidens orden. Ulempen er at
du må lese av forgjengerne for å finne ut hvilken node i UU som var best.

Relaksering
avstand[v]=min(avstand[v], avstand[u]+w(u,v))\text{avstand}[v] = \min\bigl(\text{avstand}[v],\ \text{avstand}[u] + w(u, v)\bigr)

Kostnad O(1)O(1). Hele korteste-vei-teorien er denne ene linja gjentatt i riktig
rekkefølge — BFS, Dijkstra, DAG-metoden og Bellman-Ford skiller seg bare i
rekkefølgen de relakserer kantene i.

Dijkstras invariant

Når en node tas ut av prioritetskøen, er avstanden til den endelig.

Begrunnelsen: enhver annen vei dit må gå gjennom en node som fortsatt ligger i
køen, og den har minst like høy avstand — pluss en kant som ikke kan være negativ.
Fjerner du ikke-negativitets-kravet, faller hele argumentet, og med det
algoritmen.

`forgjenger`-arrayet

For hver node: hvilken node vi kom fra da avstanden sist ble forbedret.

Selve veien leses ut baklengs fra målet til startnoden, i O(V)O(|V|). Uten dette
arrayet har du bare avstandene, ikke rutene — og oppgaver som ber om «hvilken vei»
krever begge deler.

BFS som korteste vei

I en uvektet graf (eller når alle vekter er like) gir BFS korteste avstand målt i
antall kanter, i O(V+E)O(|V| + |E|).

Grunnen er lagvis-rekkefølgen: alle noder med avstand kk tas ut før noen med
avstand k+1k+1. På en vektet graf er dette galt — BFS teller kanter, ikke kostnad.

Prioritetskøens rolle i Dijkstra

Køen svarer på «hvilken uferdig node har lavest kjent avstand». Med binær heap
koster RemoveMin og DecreaseKey O(logV)O(\log |V|) hver.

Det er nøyaktig der logaritmefaktoren i O((V+E)logV)O((|V| + |E|)\log |V|) kommer fra. Uten
prioritetskø, med lineær leting hver runde, blir Dijkstra O(V2+E)O(|V|^2 + |E|) — som
faktisk er raskere på svært tette grafer, men ikke er standardsvaret.

Negativ sykel

En sykel der summen av kantvektene er negativ. Da finnes ingen korteste vei — hver
ny runde rundt gjør veien billigere.

Bellman-Ford oppdager det med én ekstra relakseringsrunde: gir runde V|V|
fortsatt forbedringer, finnes en negativ sykel. I IN2010 er dette et faktapunkt på
Del 1, ikke en oppgavetype på Del 2.

Enkeltkilde mot alle par

Alle fire algoritmene i dette kapitlet er enkeltkilde: én kjøring gir avstanden
fra én startnode til alle andre.

Vil du ha avstanden mellom alle par, må du kjøre en av dem én gang per node.
Algoritmer laget spesielt for alle-par-problemet er ikke IN2010-pensum — se
avgrensningen i kap. 7.3.

Mønstergjenkjenning: «nærmeste kilde»

Ser du «hvilket av flere utgangspunkt er nærmest målet», er svaret én reversert
Dijkstra
, ikke én kjøring per utgangspunkt.

Beslektede formuleringer: «hvilken av stasjonene kommer først fram», «hvilket lager
skal levere», «hvilken inngang er nærmest». Alle har samme struktur: mange
startnoder, ett mål.

Å stoppe Dijkstra tidlig

Skal du bare ha avstanden til én bestemt node tt, kan du avslutte i det tt blir
tatt ut av køen — da er avstanden endelig.

Å stoppe når tt blir oppdaget, er derimot feil: verdien kan fortsatt bli lavere.
Orden på kjøretiden endres ikke av tidlig stopp i verste tilfelle.

Uoppnåelige noder

Noder som ikke kan nås fra startnoden, beholder \infty som avstand gjennom hele
kjøringen.

Det er et gyldig svar, ikke en feil, og det skal stå i tabellen. En besvarelse som
utelater dem, har ikke oppgitt hele sluttilstanden — sensor ber om avstanden til
alle noder.

Poengtrappen på korteste vei

Samme oppgave, tre nivåer: O(V+E)O(|V| + |E|) når grafen tillater det (uvektet eller
DAG), O((V+E)logV)O((|V| + |E|)\log |V|) med Dijkstra, O(VE)O(|V| \cdot |E|) med
Bellman-Ford.

Alle tre kan være korrekte på samme graf. Bare den øverste gir full uttelling — «å
velge riktig algoritme er å velge poeng» er den mest gjentatte poengregelen i
sensorveiledningene.

Repetisjon — kapitlet på ett kort

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.

Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.