Bellman-Ford
Potrzebujesz założenia o braku nieujemnych cykli.
function BellmanFord(G, s){
dist = new int[n];
foreach(var v in V(G)){
dist[v] = inf;
}
dist[s] = 0;
for(int i = 1; i < n; i++){
foreach(var e in E(G)){
int u = e.From;
int v = e.To;
if(dist[v] > dist[u] + e.weight)
dist[v] = dist[u] + e.weight;
}
return dist;
}
}Bellman Ford wpierw szacuje odległość każdego wierzchołka od s. Następnie co następną iterację i poprawia wynik długości najkrótszych co najwyżej i elementowych spacerów od s do i.
Dowód poprawności
d_k(v) -> minimalna długość spaceru o <= k krawędziach od s do v;
d(v) -> odległość od s do v;
- w każdym momencie zachodzi
dist[v] >= d(v). Indukcja po ilości wykonańdist[v] > dist[u] + w(uv)- i = 0.
dist[s] = 0. brak ujemnych cykli, więc poprawne,dist[u] = inf > d(u)dla każdego innego u, spełniony warunek - przed wywołaniem
dist[v] = dist[u] + w(uv)spełniony jest warunekdist[u] >= d(v). Po wywołaniu pętli
dist[v] = dist[u] + w(uv) >= d(u) + w(uv). - najkrótsza ścieżka do u + odległość uv jest niekrótsza niż najkrótsza ścieżka do v, więc:
dist[v] = dist[u] + w(uv) >= d(u) + w(uv) >= d(v)- po wykonaniu linii dla każdego wierzchołka v w G spełniony jest ten warunek na mocy indukcji
- i = 0.
- po i iteracjach zewnętrznej pętli zachodzi
dist[v] <= d_i(v):- zachodzi dla i = 0, bo 0 elementowy spacer z s dojdzie tylko do s, czyli
dist[s]= 0; - załóżmy, że prawdziwe dla i = j, gdzie j < (0, n-2); weźmy dowolny wierzchołek v z G.
- niech
dist[v]to stan po j-tej iteracji, adist'[v]po j+1; - chcę pokazać że
dist'[v] <= d_j+1(v). Jeślidist[v] <= d_j+1(v), todist[v] >= dist'[v], bo w trakcie algorytmu tablica dystansu tylko zmniejsza wartości. - rozpatrzamy więc przypadek
dist[v] > d_j+1(v). Wtedy :- Niech P to najtańszy spacer o co najwyżej j+1 krawędziach z s do v. Wtedy niech P’ to spacer o jedną krawędź krótszy, od s do x. Jako że P to najkrótsza ścieżka to v, to d_j+1(v) = d_j(x) + w(xv);
- Wtedy z założenia indukcyjnego
dist[x] <= d_j(x). Więcdist[x] + w(uv) <= d_j(x) + w(uv) = d_j+1(v). Jako żedist[x] + w(uv) = dist'[v]to pociągadist'[v] <= d_j+1(v).
- zachodzi dla i = 0, bo 0 elementowy spacer z s dojdzie tylko do s, czyli
- Dla każdego wierzchołka zachodzi d(v) = d_n-1(v). Ponieważ jeśli nie, to musimy mieć ujemny cykl, sprzeczne z założeniem. Po n-1 iteracjach zewnętrznej pętli:
d(v) <= dist[v] <= d_n+1(v) = d(v), więcdist[v] = d(v). CNU.
Złożoność O(nm);
Dijkstra
Zakłada brak nieujemnych krawędzi
function Dijkstra (G, s){
dist[v] = new int[n];
for(int i = 0; i < n; i++){
dist[v] = inf;
}
dist[s] = 0;
var q = new queue<int,int>(dist);
q.add(s);
while(q.count > 0){
var u = q.pop();
foreach(var v in g.outneighbors(u) in q){
if(dist[v] > dist[u] + w(uv)){
dist[v] = dist[u] + w(uv);
q.decreasekey(v, dist[v]);
}
}
}
return dist;
}Dowód poprawności
W dowodzie wykorzystujemy dwa niezmienniki:
- dla każdego w !< Q zachodzi
dist[w] = d(w) - dla każdego w < Q
dist[w]to długość najkrótszej ścieżki od s do w prowadzącej tylko przez wierzchołki spoza Q.
Dowód opiera się na indukcji wobec wykonań pętli while.
- przed pierwszym wykonaniem, wszystkie wierzchołki są w kolejce, więc nie da się sformułować ścieżki z wierzchołków poza kolejką
- 1 i 2 spełnione przed iteracją i; q i dist to stany przed iteracja petli, q’ i dist’ po iteracji.
- wpierw pokażemy że dla każdego w !< Q’ spełniony jest warunek
dist[w] = d(w). Niech u to wierzchołek wybrany w tej iteracji pętli.dist'[u] = dist[u]ponieważ w pętli modyfikujemy tylko odległości sąsiadów u. Trzeba więc pokazać, żedist[u] = d(u). - Weźmy najkrótszą ścieżkę z s do u i nazwijmy ją P. Następnie zdefiniujmy x, czyli pierwszy wierzchołek na P, który jeszcze leży w Q. Niech ścieżka P’ to odcinek między s a x. Z własności optymalnej podstruktury, P’ musi być najkrótszą ścieżką do x (jeśli istnieje krótsza do x, to wtedy bierzemy ją i końcówkę z (P - P’) i otrzymujemy szybsza ścieżkę do u, co jest sprzeczne z założeniem). Jako że x jest pierwszym wierzchołkiem na P w Q, to oznacza,że każdy wierzchołek przed nim jest już spoza Q. Wykorzystując niezmiennik drugi, wiemy więc że d(x) = w(P’) =
dist[x]. Na podstawie tego:
-d(u) <= dist[u] <= dist[x] = d(x) <= d(u)
- pierwsza nierówność wynika z tego żedist[u]zawsze trzyma odległość jakiejś ścieżki z s do u, może nie najlepszej
- druga nierówność wynika z minimalności u w kolejce, jako że u było wybrane do wyjęcia to jest to spełnione
- trzecia nierówność wynika z tego, że dla nieujemnych krawędzi dystans najkrótszej ścieżki od v_1 do v_n jest zawsze <= dystansowi ścieżki v_1 do v_n+j;
- zatemdist[u] = dist'[u] = d(u)zatem niezmiennik pierwszy zachowany - drugi niezmiennik można rozpatrzyć przypadkami. Niech u takie samo, a w leży jeszcze w Q. Wtedy:
- nie ma krawędzi uw -> nie ma opcji na poprawienie odległości, niezmiennik zachowany przez brak zmiany
- jest krawędź uw ale odległość
dist'[w] = dist[w]-> wtedy uw to krawędź, ale tworzy gorsze połączenie z s do w niż rozpatrzone wcześniej => zachowanie niezmiennika przez brak zmiany stanu - jest krawędź uw i zmieniona jest odległość ->
dist'[w] < dist[w], jako że u zostało teraz wyjęte z kolejki i poprawił się stan, to oznacza że przez u istniała lepsza ścieżka do w, jako że u wyjęte to ścieżka prowadzi przez tylko wierzchołki spoza kolejki, niezmiennik zachowany.
Floyd Warshall
Założenie o braku ujemnych cykli
function FloydWarshall(G){
dist = new int[n,n] {inf};
for( i < V(G)){
for(j < V(G)){
if(ij < E(G)){
dist[i,j] = w(ij);
}
}
}
for(v < V(G)){
dist[v,v] = 0;
}
for(k < V(G)){
for(i < V(G)){
for(j < V(G)){
if(dist[i,j] > dist[i,k] + dist[k,j]){
dist[i,j] = dist[i,k] + dist[k,j];
}
}
}
}
}Dowód to zadanie na potem
Johnson
Założenie: nie ma cykli ujemnych
function Johnson(G){
tworze graf G'', który ma ekstra hiperwierzchołek, wszystkie jego krawedzie maja wage 0;
h = BellmanFord(G');
niech w'' to waga zdefiniowana jako
w''(uv) = w(uv) + h[u] - h[v];
for(int i = 0; i < n; i++){
d_i = Dijkstra (G,w'');
foreach(var v in V(G)){
dist[i,v] = d_i[v] - h(i) + h(v);
}
}
}Dowód też będzie potem

ZAD 1
function BellmanFord(G, s ,v){
dist = new int[n];
from = new int[n];
for(int i = 0; i < n; i++){
dist[i] = inf;
}
dist[s] = 0;
for(int i = 1; i < n; i++){
foreach(var e in E(G)){
int u = e.From;
int w = e.To;
if(dist[w] > dist[u] + w(e)){
dist[w] = dist[u] + w(e);
from[w] = u;
}
}
}
if (dist[v] = inf)
return null;
order = new List<int>();
temp = v;
while(temp != s){
order.add(temp);
temp = from[temp];
}
order.add(s);
return order.reverse();
}ZAD 2
function FindNegativeCycle(G){
G.AddVertex(n);
for(int i = 0; i < n; i++){
G.AddEdge(n,i, 0); // krawedz z n do i o wadze 0;
}
dist = new int[n+1];
from = new int[n+1];
for(int i = 0 ; i <=n ;i++){
dist[i] = inf;
from[i] = -1;
}
dist[n] = 0;
for(int i = 1; i <= n; i++){
foreach(var e in E(G)){
var u = e.From;
var v = e.To;
if(dist[v] > dist[u] + w(uv)){
dist[v] = dist[u] + w(uv);
from[v] = u;
}
}
}
int x = -1;
// realny check
HasNegativeCycle = false;
foreach(var e in E(G)){
var u = e.From;
var v = e.To;
if(dist[v] > dist[u] + w(uv)){
from[v] = u;
x = v;
hasNegativeCycle = true;
break;
}
}
if(!HasNegativeCycle)
return null;
for(int i = 0; i < n; i++){
x = from[x];
}
order = new list<int>();
int v = x;
do{
order.add(x);
x = from[x]
} while(v != x);
return order;
}ZAD 3
A^k [i,j], ponieważ to jest A^2[i,j] = ∑_x A[i,x] * A[x,j] , czyli suma wszystkich połączeń z i do x i x do j. Potem schodząc na wyższe potęgi dostajemy odpowiedź dla dłuższych spacerów.
ZAD 4
function SortTopological(G, u ,v){
order = new list<int>();
visited = new int[n];
for(int i = 0; i < n; i++){
if(!visited(i))
DFS(i);
}
dist = new int[n];
for(int i = 0; i < n; i++){
dist[i] = inf;
}
dist[u] = 0;
foreach(var w in order){
if(dist[w] == inf) continue;
foreach(var p in N(w)){
if(dist[p] > dist[w] + w(wp))
dist[p] = dist[w] + w(wp);
}
}
return dist[v];
}
function DFS(v){
visited[v] = true;
foreach(var u in N(v)){
if(!visited[u]){
DFS(u);
}
}
order.add(v);
}
ZAD 7
Dowód Floyda Warshalla, wpierw algortym
function FloydWarshall(G){
dist = new int[n,n];
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
dist[i,j] = inf;
}
dist[i,i] = 0;
}
foreach(var e in E(G)){
dist[e.from,e.to] = e.weight;
}
for(int k = 0; k < n; k++){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(dist[i,j] > dist[i,k] + dist[k,j])
dist[i,j] = dist[i,k] + dist[k,j];
}
}
}
return dist;
}Dowód poprawności indukcyjny.
Niezmiennik: dist[i,j] <= d_l(i,j). Pokażę, że jest zachowany przed i po każdej iteracji pętli zewnętrznej.
Baza: dist[i,j] <= d_0(i,j) , zbiór pusty, więc nie ma żadnych wewnętrznych wierzchołków w ścieżce z i do j. Jako że tablica jest zainicjalizowana długościami każdej krawędzi, to jeśli istnieje połączenie i-j, znajduje się w tablicy przed pierwszą iteracją pętli z k, spełnione.
Krok indukcyjny (względem ilości wykonań pętli zewnętrznej): Niech dist to stan tablicy przed wykonaniem l’tej iteracji, a dist' to stan po wykonaniu l’tej iteracji. Możliwe są przypadki:
d_l(i,j) = d_l-1(i,j). Oznacza to, że dodanie l-1 wierzchołka do puli możliwych wierzchołków wewnętrznych ścieżki między i a j nie poprawiło wyniku. Możemy założyć, żedist'[i,j] <= dist[i,j]bo kod nigdy nie zwiększa wyniku. W takim wypadku:
dist'[i,j] <= dist[i,j] <= d_l-1(i,j) = d_l(i,j)czyli warunek spełniony.d_l(i,j) < d_l-1(i,j). Dodanie wierzchołka l-1 do puli wierzchołków wewnętrznych poprawi wynik. Jako żed_l(i,j)to długość najkrótszej ścieżki od i do j a właśnie założyliśmy że zawiera l-1 wierzchołek wewnątrz, to:
d_l(i,j) = d_l-1(i,l-1) + d_l-1(l-1,j), dlatego że ścieżki od i do l-1 zawierają tylko pulę wierzchołków {0, … , l-2} wewnątrz ścieżki. Jako że ścieżka od i do j używa l-1 dokładnie raz (brak ujemnych cykli), to dzieli w taki sposób ścieżkę i do j na dwie części. Z tego wynika:
dist'[i,j] = dist[i,l-1] + dist[l-1,j] <= d_l-1(i,l-1) + d_l-1(l-1, j) = d_l(i,j).
Po n-1 iteracjach pętli otrzymujemy:
dist[i,j] <= d_n(i,j). d_n(i,j) to najkrótsza ścieżka korzystająca z możliwie wszystkich wierzchołków wewnętrznych, więcd_n(i,j) = d(i,j), co implikujedist[i,j] <= d(i,j).
Jako żedist[i,j]w każdej iteracji pętli przetrzymuje odległość jakiejś ścieżki między i a j (
na początku są to tylko wagi krawędzi lub zera i nieskończoności, a po każdej następnej iteracji każda relaksacja łączy te wyniki w dłuższe spacery/ścieżki, to wynik odpowiada jakiemuś spacerowi bądź ścieżce, jeszcze dokładniej:- indukcja przed wykonaniem relaksacji:
- baza, jeszcze nie wykonana żadna relaksacja, jedyne skonczone
dist[i,j]to 0, lub wagi krawędzi dla wierzchołków połączonych ze sobą, więcdist[i,j] >= d(i,j) - krok indukcyjny: weźmy dowolne
(i,j)przed wykonaniem relaksacji z wierzchołkiemk. Tylko(i,j)relaksowane, więc inne pary spełniają ten warunek. dist[i,j] = dist[i,k] + dist[k,j] >= d(i,k) + d(k,j).- Weźmy najkrótszą ścieżkę z i do j. Dla każdego k
d(i,k) + d(k,j) >= d(i,j), ponieważ najkrótsza ścieżka może prowadzić przez k (wtedy k dzieli ścieżke na właśnie te dwie długości), albo może być krótsza. Więc: dist[i,j] = dist[i,k] + dist[k,j] >= d(i,k) + d(k,j) >= d(i,j).- Z indukcji spełnione dla każdej ilości wykonań relaksacji, więc spełnione na końcu algorytmu dla każdej pary wierzchołków.
), to automatycznied(i,j) <= dist[i,j] <= d(i,j)co kończy dowód.
- baza, jeszcze nie wykonana żadna relaksacja, jedyne skonczone