Professional Documents
Culture Documents
Cac Thuat Toan Co Ban
Cac Thuat Toan Co Ban
1
THUẬT TOÁN SẮP XẾP QUICKSORT
Bài toán:
Cho dãy số A1, A2, …, An. Hãy sắp xếp dãy số đã cho tăng dần.
Ý tưởng:
Giả sử dãy A(n) đã sắp xếp tăng dần.
Nhận xét: Với mỗi đoạn con (L .. H) thuộc dãy, chọn 1 phần tử bất
kỳ thuộc đoạn làm “chốt”, ta luôn có:
Mọi phần tử bên trái chốt không vượt quá chốt;
Mọi phần tử bên phải chốt không bé hơn chốt
Ví dụ:
4 5 6 7 8 8 10
1 2 3 4 5 6 7
L Chot H
THUẬT TOÁN SẮP XẾP QUICKSORT
Từ nhận xét trên, thuật toán QuickSort sắp xếp dãy tăng dần theo ý
tưởng:
Trong dãy A(n) , chọn 1 phần tử bất kỳ làm “chốt”, sau đó sắp xếp
sao cho:
Mọi phần tử bên trái chốt không vượt quá chốt;
Mọi phần tử bên phải chốt không bé hơn chốt
Ví dụ:
5 10 7 8 4 6 8
1 2 3 4 5 6 7
Chot
Chot
5 8 7 6 4 8 10
1 2 3 4 5 6 7
THUẬT TOÁN SẮP XẾP QUICKSORT
Trong đoạn con trái A(1..j) , chọn 1 phần tử bất kỳ làm “chốt”, sau
đó sắp xếp sao cho: Mọi phần tử bên trái chốt không vượt quá chốt;
mọi phần tử bên phải chốt không bé hơn chốt.
Trong đoạn con phải A(i..n) , chọn 1 phần tử bất kỳ làm “chốt”, sau
đó sắp xếp sao cho: Mọi phần tử bên trái chốt không vượt quá chốt;
mọi phần tử bên phải chốt không bé hơn chốt.
Quá trình cứ thế tiếp diễn đến khi sắp xếp xong.
Ví dụ:
Chot
5 8 7 6 4 8 10
1 2 3 4 5 6 7
j i
5 10 7 8 4 6 8
1 2 3 4 5 6 7
5
L Chot H
THUẬT TOÁN SẮP XẾP QUICKSORT
- Tìm cách sắp xếp sao cho các phần tử nhỏ hơn (hoặc bằng) “chốt”
được đặt về bên trái “chốt”, các phần tử lớn hơn (hoặc bằng) “chốt”
được đặt bên phải “chốt”. Cách làm: thực hiện lặp các bước
B1. từ trái sang phải, tìm i sao cho A[i] ≥ chot
B2. từ phải sang trái, tìm j sao sao A[j] ≤ chot
B3. Nếu i < j (tồn tại nghịch thế) thì đổi chỗ A[i] với A[j]; rồi tăng i
và giảm j
Nếu i = j, tức A[i] và A[j] chỉ là một, thì chỉ tăng i và giảm j
B4. Nếu i ≤ j, quay lại B1, còn i>j thì thoát lặp.
5 10 7 8 4 6 8
1 2 3 4 5 6 7
B1, B2 Chot
i j
5 8 7 8 4 6 10
1 2 3 4 5 6 7
B3, B4 L Hj
i
THUẬT TOÁN SẮP XẾP QUICKSORT
5 8 7 8 4 6 10
1 2 3 4 5 6 7
B1, B2
L Chot H
i j
5 8 7 8 4 6 10
1 2 3 4 5 6 7
B1, B2
L i j H
5 8 7 6 4 8 10
1 2 3 4 5 6 7
B3, B4
L i j H
5 8 7 6 4 8 10
1 2 3 4 5 6 7
B3, B4
L j H
i
THUẬT TOÁN SẮP XẾP QUICKSORT
- Tới đây, do i > j nên thoát vòng lặp.
- Dãy được phân chia thành 2 đoạn con:
+ Đoạn bên trái gồm các phần tử (L .. j) ≤ “chốt”.
+ Đoạn bên phải gồm các phần tử [i .. H) ≥ “chốt”
Chot = 8
5 8 7 6 4 8 10
1 2 3 4 5 6 7
j i
L H
Đoạn con trái
Đoạn con phải
8
THUẬT TOÁN SẮP XẾP QUICKSORT
- Tiếp tục sắp xếp hai đoạn con được tạo thành (có độ dài ngắn hơn độ
dài đầu) bằng phương pháp tương tự.
- Quá trình cứ thế tiếp tục cho tới khi đoạn con cần sắp xếp có độ dài
nhỏ hơn 2 (đoạn con nhỏ nhất).
Độ phức tạp:
- Thuật toán QuickSort có độ phức tạp trung bình là O(nlogn).
- Có thể sắp xếp dãy số gồm 105 phần tử trong thời gian không quá 1
giây.
5 8 7 6 4 8 10
1 2 3 4 5 6 7
L j i H
If i<=j then
Begin
If i<j then DoiCho(A[i],A[j]);
Inc(i); Dec(j);
End;
Until i>j;
int main()
{
// tham chiếu mở file input
freopen("sort.inp","r",stdin);
// tham chiếu mở file output
freopen("sort.out","w",stdout);
// khai báo
int n;
int x;
vector <int> a;
a.push_back(oo); // gán a[0] = oo
// đọc dữ liêu
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x;
a.push_back(x);
}
return 0;
}
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
Bài toán: Cho dãy số A1, A2, …, An và số X. Hãy tìm phần tử của dãy
có giá trị bằng X.
Giải:
- Sắp xếp dãy số đã cho tăng dần (giảm dần).
- Đặt L := 1; H := n;
- Bước lặp:
B1. Chọn vị trí “ở giữa” dãy.
Mid := (L+H) div 2;
Tìm X = 10
5 10 7 8 4 6 8
1 2 3 4 5 6 7
Sắp xếp
4 5 6 7 8 8 10
1 2 3 4 5 6 7
15
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
Tìm X = 10
A[Mid] = 7 < 10
4 5 6 7 8 8 10
1 2 3 4 5 6 7
L Mid = 4 H
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
4 5 6 7 8 8 10
1 2 3 4 5 6 7
L Mid = 6 H
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
Tìm X = 10
A[Mid] = 10
4 5 6 7 8 8 10
1 2 3 4 5 6 7
Mid = 7
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
Hàm tìm kiếm giá trị X trong dãy A1…An, trả về chỉ số tìm thấy:
Function BinarySearch(X:longint):longint;
Var L,H,Mid: longint;
Begin X
1 Mid n
L := 1; H := n;
While (L<=H) do
Begin 1 Mid X n
Mid := (L+H) div 2;
If A[Mid] = X then Exit(Mid);
If A[Mid] < X then L := Mid+1
Else H := Mid - 1;
End; 1 X Mid n
Exit(0);
End;
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
#include <iostream> // std::cin, cout
#include <algorithm> // std::sort
#include <vector> // std::vector
/*
#include <bits/stdc++.h> // include này bao gồm các include trên
*/
int n;
int x;
int ans;
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
int main()
{
// tham chiếu mở file input
freopen("BinarySearch.inp","r",stdin);
// tham chiếu mở file output
freopen("BinarySearch.out","w",stdout);
// đọc file
cin>>n>>x; // x là giá trị cần tìm
int ai;
vector <pair<int,int>> a; // vector của cặp
for(int i=0;i<n;i++)
{
cin>>ai;
a.push_back(make_pair(ai,i));
}
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
// sắp xếp vector a tăng dần
sort(a.begin(), a.end());
ans = -1;
// tìm x bằng TKNP
int L = 0;
int H = n-1;
int Mid;
while (L <= H)
{
Mid = (L + H) / 2;
if (x == a[Mid].first)
{
ans = a[Mid].second;
break;
}
else
{
if (x < a[Mid].first) H = Mid - 1;
if (x > a[Mid].first) L = Mid + 1;
}
}
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
return 0;
}
THUẬT TOÁN TÌM KIẾM NHỊ PHÂN
Nhận xét:
Tìm kiếm
Cho a(n) là dãy số nguyên a1,a2,...,an và dãy các giá trị p1,p2,...,pm.
Hãy tìm vị trí xuất hiện của mỗi giá trị pi trong dãy a(n).
Dữ liệu vào từ file văn bản TIMKIEM.INP gồm nhiều dòng:
Dòng đầu tiên chứa hai số nguyên n, m (1 ≤ n, m ≤ 105);
Dòng thứ hai ghi dãy số nguyên a1,a2,...,an (|ai| ≤ 106, ai ≠ aj với i ≠j) ;
Dòng thứ ba ghi dãy giá trị p1,p2,...,pm (|pi| ≤ 106);
Các số trên cùng dòng viết các nhau một dấu cách.
Kết quả ghi ra file văn bản TIMKIEM.OUT gồm m dòng, dòng thứ i
ghi vị trí tìm gặp giá trị pi xuất hiện trong dãy a1,a2,...,an . Nếu không
tìm thấy pi thì ghi số 0.
Ví dụ:
TIMKIEM.INP TIMKIEM.OUT
75 3
6 -3 1 8 -1 7 -7 5
1 -1 8 2 -7 4
0
7
65 2
6 -7 -1 8 3 7 3
-7 -1 8 2 -7 4
0
2
Ràng buộc: có 50% test ứng với 50% số điểm với n ≤ 103
có 50% test ứng với 50% số điểm với n ≤ 105
26
27
BÀI TẬP ÁP DỤNG
Ràng buộc: có 50% test ứng với 50% số điểm với n ≤ 103
có 50% test ứng với 50% số điểm với n ≤ 105
28
29
BÀI TẬP ÁP DỤNG
Doanh thu
Công ty L là một công ty liên doanh nước ngoài, chuyên sản xuất
các mặt hàng giày da thuộc Khu công nghiệp H, họ theo dõi việc
kinh doanh bằng cách ghi lại doanh thu đạt được ở mỗi ngày.
Theo đó, doanh thu đạt được của công ty sau N ngày là một dãy
số gồm N số nguyên a1, a2, ..., aN; trong đó ai là doanh thu của
công ty ở ngày thứ i (lưu ý doanh thu có thể là số âm).
Trước diễn biến phức tạp của việc đại dịch Covid-19 tái bùng phát
ở các nước châu Âu, công ty cần đánh giá lại hiệu quả kinh doanh
trong N ngày nêu trên. Giám đốc công ty muốn biết khoảng thời
gian ngắn nhất gồm những ngày liên tiếp nhau có tổng doanh thu
đạt được không thấp hơn mức doanh thu phải đạt S.
Yêu cầu: Cho trước dãy số a1, a2, ..., aN và S, bạn hãy giúp giám đốc công
BÀI TẬP ÁP DỤNG
ty tìm câu trả lời cho vấn đề đặt ra.
Dữ liệu: vào từ file văn bản DOANHTHU.INP có nội dung:
- Dòng đầu tiên chứa hai số nguyên dương N, S (1 ≤ N ≤ 104, 0 ≤ S ≤
109);
- Dòng thứ hai là dãy số nguyên a1, a2, ..., aN (|ai| ≤ 104).
Các số trên cùng dòng được ghi cách nhau bởi dấu cách.
Kết quả: ghi ra file văn bản DOANHTHU.OUT một số duy nhất là độ dài
dãy con ngắn nhất tìm được thỏa yêu cầu nêu trên. Nếu không tìm được dãy
con nào thì ghi -1.
Ví dụ:
DOANHTHU.INP DOANHTHU.OUT Giải thích
6 10 2 Dãy con thỏa yêu cầu là
0 0 -2 0 5 6 dãy a5 .. a6.
20 -1 Không tìm được dãy con
-4 -6 thỏa yêu cầu.
30
BÀI TẬP ÁP DỤNG
Lời giải O(n ) 2
3 000
001
010
011
100
101
110
111
Lời giải:
- Mỗi cấu hình có n phần tử, mỗi phần tử có 2 khả năng chọn lựa là 0 hoặc 1.
- Thủ tục Try duyệt toàn bộ sinh ra tất cả các cấu hình như sau:
Procedure Try(i:longint); {Thủ tục xây dựng phần tử thứ i của cấu hình}
Var j: longint;
Begin
For j:=0 to 1 do {Xét các giá trị có thể chọn cho phần tử i}
Begin
a[i]:= j; {Thử gán phần tử thứ i giá trị là j}
If (i=n) then Ketqua {Nếu đã xây dựng đến phần tử cuối cùng thì
thông báo kết quả}
Else Try(i+1); {xây dựng phần tử kế tiếp của cấu hình}
End;
End;
Hình minh họa:
Cây tìm kiếm gốc Try(1) với n = 3 có dạng sau:
- Thủ tục thông báo một cấu hình tìm được:
Procedure Ketqua;
Var i: longint;
Begin
For i:=1 to n do Write(a[i],#32);
End;
- Lời gọi bắt đầu thủ tục duyệt các cấu hình là Try(1):
BEGIN
Nhap;
Try(1);
END.
Yêu cầu: Hãy giúp Vinh giải quyết bài toán đặt ra
Dữ liệu vào:
Dòng đầu chứa số nguyên dương m;
Dòng thứ hai chứa m số nguyên không âm b1, b2, ..., bm (bi ≤ 109).
Kết quả: Ghi ra số nguyên k là số phần tử bị loại bỏ. Ghi số 0 nếu dãy đã
cho là SEQ198.
Ví dụ:
Input
6
731921
Output
2
Ràng buộc:
Có 50% số test ứng với 50% số điểm của bài có m ≤ 20.
Có 50% số test còn lại ứng với 50% số điểm của bài có m ≤ 2000.
41
Hướng dẫn:
Với mỗi dãy nhị phân sinh ra, ta kiểm tra xem dãy con được giữ lại có là
dãy SEQ198 không?
Nếu có, ta đếm số phần tử bị xóa (đếm số bit 0), rồi cập nhật “kỷ lục”
mới (nếu có).
IF Dem < Smin THEN Smin := Dem;
TỔ HỢP CHẬP K CỦA N PHẦN TỬ
Một tổ hợp chập k của n phần tử là một tập con gồm k phần tử của
tập n phần tử.
Chẳng hạn tập {1,2,3,4} có các tổ hợp châp 2 là:
{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}.
Chú ý, tổ hợp không phân biệt thứ tự giữa các phần tử, do đó tập
{1,2} và tập {2,1} chỉ được coi là một tổ hợp. Ta thường liệt kê các
phần tử của tổ hợp theo thứ tự tăng dần.
Yêu cầu: Cho trước tập A = {1,2,...n} và số k, hãy xác định tất cả các
tổ hợp chập k của n phần tử của A.
- Dữ liệu vào từ file văn bản TOHOP.INP gồm một dòng duy nhất
chứa hai số nguyên dương n và k (1 ≤ k ≤ n ≤ 20).
- Kết quả ghi ra file văn bản TOHOP.OUT gồm nhiều dòng, mỗi
dòng ghi một tổ hợp tìm được.
Ví dụ:
TOHOP.INP TOHOP.OUT
42 12
13
14
23
24
34
Lời giải:
Mỗi tổ hợp là một tập con thỏa các điều kiện sau:
- Tập con gồm k phần tử {x1,x2,...,xk};
- xi lấy giá trị trong tập {1,2,...,n} và thỏa điều kiện:
+ xi-1 < xi với mọi 1 ≤ i ≤ k; (vì tổ hợp không phân biệt thứ tự
nên ta liệt kê theo thứ tự tăng dần)
+ xi ≤ (n-(k-i)) // giải thích: phía sau i còn (k-i) phần tử, do đó giá
trị của xi chỉ có thể lấy tối đa là (n-(k-i)).
Để tính luôn được cho x1, ta bổ sung x0 = 0.
• Số tổ hợp chập k của n phần tử được tính bởi công thức:
n!
Cnk
k!(n k )!
Một công ty thương mại có n cửa hàng dọc theo một đoạn đường quốc lộ được
đánh số từ 1 đến n (tính từ đầu đường). Li là khoảng cách của cửa hàng thứ i đến
đầu đường. Hiện tại, không có cửa hàng nào trong số n cửa hàng của công ty có
kho chứa hàng cả. Thời gian tới, công ty sẽ đặt K kho hàng tại một số cửa hàng để
thuận tiện cho việc phân phối hàng hóa. Sau khi đặt các kho hàng; nếu một cửa
hàng có kho thì không cần phải đi lấy hàng, ngược lại thì phải đến lấy hàng ở cửa
hàng có kho gần nhất.
Yêu cầu: Cho trước n và số K, hãy giúp công ty chọn K cửa hàng để đặt kho sao
cho quãng đường đi lấy hàng dài nhất của các cửa hàng không được đặt kho là
ngắn nhất.
Một hoán vị của tập n phần tử {1,2,3,…,n} là một cách xếp đặt có thứ
tự vị trí của n phần tử của tập hợp.
Chẳng hạn tập {1,2,3} có các hoán vị là:
(1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), (3,2,1).
Yêu cầu: Cho trước tập A={1,2,...,n}, hãy xác định tất cả các hoán vị
của n phần tử của A.
- Dữ liệu vào từ file văn bản HOANVI.INP gồm một số nguyên
dương n (1 ≤ n ≤ 10)
- Kết quả ghi ra file văn bản HOANVI.OUT gồm nhiều dòng, mỗi
dòng ghi một hoán vị tìm được.
Ví dụ:
HOANVI.INP HOANVI.OUT
3 123
132
213
231
312
321
Lời giải:
Mỗi hoán vị là một cấu hình thỏa các điều kiện sau:
- Là cấu hình gồm n thành phần (x1,x2,...,xn);
- xi lấy giá trị trong tập {1,2,...,n};
- xi ≠ xj với mọi i ≠ j. Có phân biệt thứ tự các thành phần.
* Số các hoán vị của tập n phần tử được tính bởi công thức:
P = n!
* Độ phức tạp thuật toán là O(n!). Chạy hiệu quả với độ lớn
của n ≤ 10.
Procedure Try(i:longint);
Var j: longint;
Begin
For j:=1 to n do
If Free[j] then
Begin
x[i]:= j;
Free[j] := False;
If (i=n) then Ketqua
Else Try(i+1);
Free[j] := True;// quay lui
End;
End;