Site-ul de programare olimpiada

Graficele: găsi cele mai scurte trasee

Această secțiune discută graficele îndreptate ponderate. Un graf neorientat poate fi considerat ca un caz special de orientare, și multigraphs înlocuiască convenționale, și sunt înlocuite cu cele mai ieftine coaste lor multirebra.

Calea cea mai scurtă dintre două noduri se numește o cale între ele, că suma greutăților marginilor care aparțin acestei căi este minimă.

Rețineți că, între anumite noduri nu poate fi calea cea mai scurtă. Acest lucru este posibil în cazul în care există o cale între ele conținând ciclu greutate negativ. Trecând prin acest ciclu putem pe termen nelimitat pentru a reduce greutatea totală a lanțului.

Dacă există o comandă rapidă, și există calea cea mai scurtă simpla conectare datele vertex (de exemplu, în cazul în care aruncarea inițială a tuturor ciclurilor de lungime zero).

Multe probleme de olimpiada se fierbe în jos pentru a găsi cele mai scurte căi. De obicei, ia în considerare următoarea problemă: dat un grafic cu greutăți ale marginilor - numere non-negativ, pe care doriți să găsiți calea cea mai scurtă dintre două noduri. Acesta este rezolvată cu ajutorul algoritmului lui Dijkstra. Dacă graficul de greutate negativă poate cuprinde nervuri, se aplică algoritmul Ford-Bellman. De asemenea, permite să se determine dacă există un ciclu negativ în grafic. Dacă doriți să găsiți cele mai scurte trasee între toate perechile de noduri, apoi se aplică algoritmul Floyd-Uorshella.

În această secțiune, vom presupune că, dacă distanța dintre (u, v) = INF (infinit), atunci graficul nu are nici o cale între (u, v). valoare constantă INF ar trebui să fie suficient de mare, dar este recomandabil să-l aleagă, astfel încât, de exemplu, expresia INF + INF nu ar fi overflow.

matricea adiacenta va fi după cum urmează: a_uv = 0 dacă u = v, altfel lungimea marginilor (u, v), dacă există, altfel INF.

Algoritmul lui Dijkstra

Algoritmul lui Dijkstra vă permite să găsiți calea cea mai scurtă de la un anumit vârf la toate celelalte noduri pe grafic. Algoritmul este format din următoarele etape:

  1. Pune toate nodurile (cu excepția original), dist distanța (v) = INF, pentru dist inițială (start) = 0.
  2. Selectați nodurile nemarcate V, cu o distanță minimă. Dacă un astfel de nod nu, atunci de ieșire.
  3. Mark v.
  4. Pentru toate nodurile nemarcate adiacente v, încercați să îmbunătățească estimarea distanței.
  5. Reveniți la pasul 2.

Punerea în aplicare pe Java:

0000: int INF = Integer. MAX_VALUE / 2; // "Infinity"
0001: int vNum; // numărul de noduri
0002: int [] [] grafic; // matrice adiacență
0,003:
0,004 / * pentru Dijkstra O (V ^ 2) * /
0005: void Dijkstra (int start) <
0,006: boolean [] folosit = new boolean [vNum]; // serie de mărci
0,007: int [] dist = new int [vNum]; // lungime matrice. dist [v] = minimalnoe_rasstoyanie (start, v)
0,008:
0,009: umple (INF dist.); // distanta ustanaavlivaem la toate nodurile de INF
0,010: dist [start] = 0; // pentru a seta vertexul inițial 0
0,011:
0,012: pentru (;;) <
0,013: int v = - 1;
0,014: pentru (int nv = 0; nv dist [nv])) // alege cele mai apropiate nodurile nemarcate
0,016: v = nv;
0,017: if (v == - 1) pauză; // cel mai apropiat nod nu a fost găsit
0,018: folosit [v] = true; // marchează
0,019: pentru (int nv = 0; nv

Menționăm că algoritmul nostru a găsit doar costul mai scurte drumuri, și nu calea între vârfurile de interes pentru noi. De fapt, acest lucru este destul de des, dar dacă doriți să găsească o cale, atunci algoritmul Dijkstra ar trebui să fie modificat. Introducem o altă caracteristică - precedentă (v) = p - vertex la care distanța a fost îmbunătățită la v în pasul 4, algoritmul original. De fapt, dacă combina toate calea cea mai scurtă de la vârful inițial, ele formează un copac. În partea de sus a oricărui copac, cu excepția rădăcinii are exact un strămoș. La sfârșitul algoritmului, anterioare (v) - și este acest strămoș. Cunoscând strămoșii, pot fi ușor recuperate și calea în sine.

Fie V - numărul de noduri, E - numărul de muchii. Menționăm că comportarea asimptotică a algoritmului este O (V ^ 2). Dar graficele descărcate pot fi modificate algoritmul lui Dijkstra. În primul rând, dacă respingem matricea de adiacenta, și trece la liste, relaxarea va necesita operații numai O (E). Pentru a accelera la cel mai apropiat Vertex obține structuri speciale de date necesare pentru a se aplica (de exemplu, RMQ sau buclate). Apoi obținem comportarea asimptotică O (ElogV).

Punerea în aplicare a algoritmului lui Dijkstra cu RMQ pe Java:

0000: int INF = Integer. MAX_VALUE / 2; // "Infinity"
0001: int vNum; // numărul de noduri
0002: Graficul MultiList; // Count Descriere
0,003:
0,004 / * pentru Dijkstra O (E log V) * /
0005: void dijkstraRMQ (int de start final int.) <
0,006: boolean [] folosit = new boolean [vNum]; // serie de mărci
0,007: int [] prev = new int [vNum]; // matrice de strămoși
0,008: int [] dist = new int [vNum]; // matrice de distante
0,009: RMQ RMQ = new RMQ (vNum); // RMQ
0010:
0,011: / * Inițializare * /
0012: umple (precedentă - 1.);
0013: se completează (INF dist.);
0,014: RMQ. set (. începe să dist [start] = 0);
0015:
0,016: pentru (;;) <
0,017: int v = RMQ. minIndex (); // alege cel mai apropiat Vertex
0,018: if (v == - 1 || v == final) se va rupe; // dacă nu este găsit, sau este sfârșitul, atunci du-te
0,019:
0,020: folosit [v] = true; // eticheta nodului selectat
0,021: RMQ. set (v INF.); // și a reseta valoarea la RMQ
0,022:
0,023: pentru (. Int i = grafic cap [v]; i = 0; i = graficul următor [i] ..) 0,024: int nv = grafic. vert [i];
0025: costul int = grafic. costuri [i];
0,026: if (folosit [nv]. dist [nv]> dist [v] + cost) 0,027: ​​RMQ. set (. nv dist [nv] = dist [v] + cost); // îmbunătățirea acesteia
0,028: prev [nv] = v; // eticheta strămoș
0,029>
0,030>
0,031>
0,032:
0,033: / * Restaurare modul * /
0,034: stiva stiva = new stivă ();
0035: pentru (int v = final; v = - 1; v = prev [v].) <
0,036: stivă. împinge (v);
0,037>
0,038: int [] sp = new int [stivă. size ()];
0039: pentru (int i = 0; i 0; v / = 2) <
0088: int l = 2 * v;
0089: int r = l + 1;
0090: if (val [l]

Bellman-Ford algoritm

Bellman-Ford algoritm nu este foarte rapid (O (VE)), dar care rulează pe grafice și margini negative. Algoritmul este destul de simplu: distanța până la punerea vârfurilor originale - 0 pentru INF rămasă, apoi V - 1 ori pentru a face tot felul de relaxare (acestea vor fi exact E). Puteți face o optimizare în cazul în care etapa actuală nu este în măsură să facă orice relaxare - stop. În cazul în care (V - 1) iterație poate efectua relaxarea, graficul are un ciclu de greutate negativ (o puteți găsi în mod explicit, dacă este depozitat strămoși).

0000: int INF = Integer. MAX_VALUE / 2; // "Infinity"
0001: int vNum; // numărul de noduri
0002: Graficul MultiList; // Count Descriere
0,003:
0,004 / * Bellman-Ford Algoritmul O (VE) * /
0005: void fordBellman (int start) <
0,006: int [] dist = new int [vNum]; // matrice de distante
0007: se completează (INF dist.); //
0,008: dist [start] = 0; // inițializează
0009: pentru (int it = 1; ea

Algoritmul Floyd-Uorshella

Acest algoritm găsește distanțele kratayshie între toate perechile de vârfuri pentru O (V ^ 3). Descrierea Algoritm: fix top k, apoi itera toate perechile posibile de noduri (i, j), și dacă a_ij> a_ik + a_kj, apoi face a_ij = a_ik + a_kj.

0000: int INF = Integer. MAX_VALUE / 2; // "Infinity"
0001: int vNum; // numărul de noduri
0002: int [] [] grafic; // matrice adiacență
0,003:
0,004 / * Algoritmul Floyd-Uorshella O (V ^ 3) * /
0005: void floydWarshall () <
0,006: int [] [] dist = new int [vNum] [vNum]; // dist [i] [j] = minimalnoe_rasstoyanie (i, j)
0,007: pentru (int i = 0; i

articole similare