You are on page 1of 4

TÌM ĐƯỜNG ĐI NGẮN NHẤT TRÊN ĐỒ THỊ

1. Đường đi
Cho đồ thị vô hướng G= (V, E), với V là tập đỉnh, E là tập cạnh của G, u, v là hai đỉnh của
G. Một đường đi độ dài k từ u đến v là dãy (u = x0, x1, x2, ...,xk = v) thỏa mãn (xi, xi+1)E với i:
(0≤i<k). Đỉnh u được gọi là đỉnh đầu, v được gọi là đỉnh cuối của đường đi.
2. Đường đi ngắn nhất Là đường đi có tổng trọng số trên đường đi nhỏ nhất.
Bài toán: Cho đồ thị vô hướng có trọng số không âm gồm N đỉnh (N≤100) và M cạnh (M≤100),
cho hai đỉnh s, t. Tìm đường đi cơ bản (không có đỉnh lặp lại) ngắn nhất từ đỉnh xuất phát s đến
đỉnh đích t ( s≠t).
* Dữ liệu vào: Vào từ File văn bản MINPATH.INP có cấu trúc
- Dòng 1: Ghi 4 số nguyên dương N, M (M, N ≤
MINPATH.INP MINPATH.OUT
10000), s, t (s, t ≤ N), lần lượt tương ứng N là số đỉnh,
6 8 1 6 6
M là số cạnh, s là đỉnh xuất phát, t là đỉnh kết thúc. 1 2 1
1->4->5->6
- M dòng tiếp theo, mỗi dòng ghi 3 số nguyên dương u, 1 4 2
1 3 2
v, c cách nhau ít nhất một dấu cách, thể hiện có cạnh 2 4 2
nối đỉnh u và v trong đồ thị có trọng số là c. 3 5 3
4 5 1
* Dữ liệu ra: Ghi vào File văn bản MINPATH.OUT 4 6 5
có cấu trúc 5 6 3
- Dòng 1: Ghi độ dài đường đi ngắn nhất từ s đến t.
- Dòng 2: Đường đi từ s đến t. (Nếu có nhiều đường đi, chỉ hiển thị một đường đi minh họa)
3. Thuật toán Dijkstra (Trên đồ thị có trọng số không âm)
3.1. Thuật toán Dijkstra
Gọi d[i] là độ dài đường đi ngắn nhất từ đỉnh xuất phát s đến i.
Gọi dd[i] =1 nếu đinht i đã đánh dấu, =0 nếu i chưa đánh dấu (tự do)
Tr[i] là đỉnh trước đỉnh i trên đường đi ngắn nhất tìm được
Dijkstr(s):
Bước 1: Khởi trị
- d[i]=  , dd[i] = 0, mọi 1=1..n ; d[s]:=0;
Bước 2: (Lặp vô hạn)
- Tìm đỉnh u chưa đánh dấu (tự do) có nhãn d[u] nhỏ nhất.
- Nếu không tìm được đỉnh u (u=0) thì thoát khỏi vòng lặp.
Ngược lại: Sửa lại nhãn các đỉnh tự do v kề với u theo công thức:
Nếu d[v]>d[u] + ts(u,v) thì ( d[v] = d[u]+ts[u,v], Tr[v] =u)
Bước 3: Tìm và in kết quả:
L[t] là độ dài đường đi ngắn nhất từ s đến t. Lần ngược mảng Tr để tìm hành trình này.
3.2 Tổ chức dữ liệu bằng priority_queue
// Biểu diễn đồ thị dùng mảng a kiểu vector
struct bg{
int x;
int ts;
};
vector <bg> a[maxn];

//Vì mỗi lần lấy ra một đỉnh tự do u có d[u] nhỏ nhất, ta sẽ sử dụng priority_queue Min h có độ
phức tạp O(log N). Vậy mỗi phần tử trong h ta phải lưu hai thông tin đỉnh u và d[u].

struct cmp
{
bool operator() (bg a, bg b)
{
return a.ts>b.ts;
}
};
priority_queue <bg, vector <bg> , cmp> h;

3.3. Cài đặt thuật toán:


void dijkstra(int s)
{
priority_queue <bg, vector <bg> , cmp> h;

d[i]=  , dd[i] = 0, mọi 1=1..n ; d[s]:=0;


Thêm (s, d[s]) vào h.
while (!h.empty())
{
- Lấy (u, d[u]) ra khỏi h.
- Nếu u là đỉnh cố định thì bỏ qua (continue;)
- đánh dấu u đã cố định
- Duyệt các đỉnh tự do v kề u
if (d[v]>d[u]+TS(u,v)
{
d[v]=d[u]+TS(u,v);
tr[v]=u; // Lưu vết đường đi
thêm(v, d[v]) vào h
}
}
}

Nhận xét:
- Các đỉnh trong h là tự do.
- Với một đỉnh u chưa xét, thì có thể (u, d[u]) bỏ vào h rất nhiều lần (tùy theo đồ thi cho), lần sau
khi bỏ vào thì có d[u] nhỏ hơn. Khi lấy đỉnh u từ h, thì đỉnh này có d[u] nhỏ nhất trong các cặp (u,
d[u]) trong h, ta sẽ đánh dấu u rồi, nên những cặp (u, d[u]) còn lại trong h, ta lấy ra và sẽ không xử
lý nữa. Do đó việc thêm(u, d[u]) vào h nhiều lần như thế sẽ không ảnh hưởng kết quả vì ta cài đặt
Nếu u đã cố định thì bỏ qua (continue;)
3.4. Chương trình giải bài toán:
#include <bits/stdc++.h>
#include <vector>
#include <queue>
#define fi "MINPATH.INP"
#define fo "MINPATH.OUT"
using namespace std;

struct bg{
int x;
int ts;
};

struct cmp
{
bool operator() (bg a, bg b)
{
return a.ts>b.ts;
}
};

const int maxn =100+3;


vector <bg> a[maxn];
int dd[maxn],d[maxn],n,m,k,tr[maxn],c[maxn];

void doc()
{
int u, v, cost;
bg w;

cin>>n>>m;
memset(a,0,sizeof(a));
for (int i=1; i<=m; i++)
{
cin>>u>>v>>cost;
w.x=v; w.ts=cost; a[u].push_back(w);
w.x=u; w.ts=cost; a[v].push_back(w);
}
}
void dijkstra(int s, int t)
{
priority_queue <bg, vector <bg> , cmp> h;
bg w;

memset(dd,0,sizeof(dd));
memset(tr,0,sizeof(tr));
for (int i=1; i<=n; i++) d[i]=2e9; d[s]=0;
w.x=s; w.ts=d[s]; h.push(w); //tạo w chứa (u,d[u])

while (!h.empty())
{
int u=h.top().x; h.pop();
if (dd[u]==1) continue;
// if (u==t) break;
dd[u]=1;
for (int i=0; i<=a[u].size()-1; i++)
{
int v=a[u][i].x;
int cost=a[u][i].ts;
if (dd[v]==0)
if (d[v]>d[u]+cost)
{
d[v]=d[u]+cost;
tr[v]=u;
w.x=v; w.ts=d[v]; h.push(w);
}
}
}
}
void xuly()
{
dijkstra(s,t);
cout<<d[t]<<endl;
int j=t; int dem=0;
while (tr[j]!=0)
{
dem++; c[dem]=j;
j=tr[j];
}
dem++; c[dem]=s;

for (int j=dem; j>1; j--) cout<<c[j]<<"->";


cout<<c[1];
}
int main()
{
freopen(fi,"r",stdin);
freopen(fo,"w",stdout);
ios_base :: sync_with_stdio();
cin.tie(0); cout.tie(0);
doc();
xuly();
}

Suy nghĩ trả lời các câu hỏi sau:


Sau khi ta tìm được đường đi ngắn nhất từ một đỉnh s đến tất cả các đỉnh còn lại:
1) Làm thế nào ta kiểm tra đỉnh u có nằm trên đường đi ngắn nhất hay không?
2) Làm thế nào tính số lượng đđnn từ đỉnh xuất phát đến đỉnh u?
3) Giả sử ta có nhiều đđnn từ (s->t) có trọng số K thì làm thế nào để biết đỉnh nào bắt buộc phải đi
qua trên đường đi ngắn nhất (nghĩa là ta bỏ đỉnh này thì ta sẽ không có giá trị đđnn bằng K).

You might also like