Professional Documents
Culture Documents
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;
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;
}
};
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;