Professional Documents
Culture Documents
Mục lục
I. MỞ ĐẦU.............................................................................................................................4
I.1. Môi trường phát triển.................................................................................................................... ..........4
I.2. Chương trình đầu tiên...................................................................................................... ......................4
I.3. Input & Output................................................................................................................ ........................5
III. LỆNH.................................................................................................................................9
III.1. Lệnh dạng biểu thức.......................................................................................................................... ...9
III.2. Khối lệnh.................................................................................................................. ............................9
III.3. Lệnh rẽ nhánh.............................................................................................................. ........................9
III.4. Vòng lặp for...................................................................................................................... ..................10
III.5. Vòng lặp while................................................................................................................. ...................13
III.6. Vòng lặp do...while........................................................................................................ .....................13
III.7. Nhảy không điều kiện......................................................................................................... ................13
IV. HÀM................................................................................................................................13
IV.1. Định nghĩa hàm................................................................................................................. .................13
IV.2. Ví dụ khai báo hàm........................................................................................................................... ..14
IV.3. Khi nào sử dụng hàm...................................................................................................... ...................14
IV.4. Hàm được gọi như thế nào?........................................................................................ ......................14
IV.5. Hàm gọi đệ quy........................................................................................................... .......................15
V. MẢNG..............................................................................................................................17
V.1. Định nghĩa............................................................................................................................ ...............17
V.2. Truy cập phần tử của mảng............................................................................................................. ....17
V.3. Khởi tạo mảng......................................................................................................................... ............17
V.4. Mảng nhiều chiều.............................................................................................................. ..................17
V.5. Sử dụng mảng làm tham số của hàm................................................................................................ ..17
VII. STRUCT........................................................................................................................17
VII.1. Định nghĩa Struct.................................................................................................... ..........................17
VII.2. Khai báo biến................................................................................................................. ...................18
VII.3. Truy cập trường...................................................................................................... ..........................18
VIII. Stream..........................................................................................................................18
VIII.1. File stream............................................................................................................... ........................18
VIII.2. String stream............................................................................................................................ ........19
VIII.3. Ghi có định dạng............................................................................................................... ...............19
2
Lập trình C++ Nguyễn Phú Quảng
XI. LỚP................................................................................................................................21
XII. MẪU...............................................................................................................................21
3
Lập trình C++ Nguyễn Phú Quảng
I. MỞ ĐẦU
C++ là ngôn ngữ mạnh, được phát triển từ ngôn ngữ C. Học C++ đem lại nhiều lợi thế cho bạn với
khối lượng mã nguồn vào loại lớn nhất trong các ngôn ngữ, cũng như tính tương đồng với hầu hết
các ngôn ngữ tựa C (C-like language).
Tài liệu này mong muốn giúp bạn có một cái nhìn tổng thể với cách tiếp cận thật ngắn gọn. Cấu trúc
sách dựa trên cuốn "C++ in a nutshell" của nhà xuất bản O'Reilly.
Tại Việt nam, đã có rất nhiều cuốn sách giới thiệu về C, C++. Trong tài liệu này, chúng tôi không
mang tham vọng xây dựng một giáo trình về C++ mà chỉ là những đúc kết kinh nghiệm mà chúng
tôi đã trực tiếp làm việc với C++, nhất là trong những bài toán thiên về giải thuật.
Mọi góp ý xin gửi về địa chỉ: nguyenphuquang@gmail.com. Xin chân thành cảm ơn mọi đóng góp
của các bạn.
4
Lập trình C++ Nguyễn Phú Quảng
Dòng 07: getchar() là hàm giúp chương trình dừng lại và đợi người dùng gõ một phím bất kỳ. Thực
chất, trong ví dụ này không cần phải dừng chương trình. Nhưng do đặc tính của môi trường DevC
và VC là màn hình output sẽ biến mất sau khi chương trình chạy xong, do đó chúng tôi đưa thêm
lệnh này để dừng chương trình lại, giúp các bạn quan sát thấy xâu ký tự in trên màn hình.
II.2. Kiểu
a. Kiểu số
• Số nguyên
- Kiểu char (1 byte): (-128 đến 127)
- Kiểu short (2 byte): (-32768 đến 32767)
- Kiểu int, long (4 byte): (-2 tỷ đến 2 tỷ)
- Tất cả các kiểu trên, nếu bổ sung unsign :
o unsign char:
o unsign short:
o unsign int:
- Việc lựa chọn kiểu dữ liệu trong bài toán phục thuộc vào vùng giá trị của biến. Khoảng giá
trị càng lớn, biến càng chiếm nhiều bộ nhớ. Chương trình C++ dịch trên DevC hoạt động
trên môi trường Windows thường là hệ điều hành 32 bit, số nguyên được sử dụng phổ biến
nhất là kiểu int.
5
Lập trình C++ Nguyễn Phú Quảng
- Ví dụ
char a;
int b = 20;
long c = 30;
unsign char d = 200;
• Số thực
- float (4 byte): Số thực độ chính xác đơn.
- double (8 byte): Số thực độ chính xác kép.
• Logic
- bool: có 2 giá trị là true và false
a. Kiểu mảng
- Mảng là một dãy các phần tử cùng kiểu được sắp liên tiếp nhau trong bộ nhớ
- Các phần tử được phân biệt với nhau bởi chỉ số, là các số tuần tự bắt đầu từ 0
- Ví dụ
int a[100]; // Khai báo mảng gồm 100 số int
// a[0] là 1 số int, phần tử đầu tiên của mảng
// a[1] là 1 số int, phần tử thứ 2 của mảng
// a[99] là 1 số int, phần tử cuối cùng của mảng
long b[10]; // Khai báo mảng gồm 10 số long
- Mảng được khai báo theo ví dụ trên là mảng 1 chiều. Chiều của mảng là số chỉ số sử dụng
để xác định một phần tử (giống như hệ tọa độ 1 chiều, 2 chiều, 3 chiều)
- Ví dụ khai bao và sử dụng mảng nhiều chiều như sau
int a[3][3]; // Khai báo a là mảng 2 chiều (giống như 1 bảng)
// a[0] là một mảng 1 chiều gồm 3 số nguyên (giống như 1 dòng của bảng)
// a[0][0] là số int đầu tiên của dòng
// a[0][1] là số int thứ 2 của dòng
// a[0][2] là số int cuối cùng của dòng
// a[1] là dòng thứ 2 của bảng
// a[2] là dòng cuối cùng của bảng
a[1][1] = 5; // Gán giá trị của số nguyên ở dòng 1, cột 1 là 5
b. Kiểu struct
Struct là kiểu dữ liệu cho phép bạn tập hợp nhiều thành phần vào trong cùng một biến. Lấy ví dụ
thông tin của một điểm gồm 2 tọa độ x, y, thông tin của một đoạn thẳng gồm điểm đầu và điểm
cuối.
Struct là kiểu dữ liệu, do đó bạn phải khai báo kiểu dữ liệu đó bằng lệnh typedef rồi mới có thể khai
báo được các biến thuộc kiểu này. Cú pháp khai báo struct như sau:
struct <Tên_kiểu> { // Khai báo kiểu struct
<Kiểu_trường_1> <Tên_trường_1>; // Trường của struct (giống khai báo biến)
<Kiểu_trường_2> <Tên_trường_2>, <Tên_trường_3>;
};
<Tên_kiểu> <biến_1>, <biến_2>; // Khai báo biến thuộc kiểu mới tạo ra
Các thành phần khai báo bên trong struct được gọi là trường (field). Một struct gồm nhiều trường,
biến thuộc kiểu struct
6
Lập trình C++ Nguyễn Phú Quảng
a. Kiểu string
b. Kiểu vector
c. Kiểu vector
II.4.2. Toán tử
8
Lập trình C++ Nguyễn Phú Quảng
III. LỆNH
III.1. Lệnh dạng biểu thức
o <Điều_kiện> có thể là
Một biểu thức logic
Một biểu thức kiểu số (số khác 0 được coi là đúng, bằng 0 là sai)
o Nếu <Lệnh> chỉ là 1 lệnh đơn, bạn không cần để trong khối chương trình bằng cặp
ngoặc {}. Tuy nhiên, việc này không được khuyến khích.
- Dạng đầy đủ: Nếu <Điều_kiện> là đúng thì thực hiện <Lệnh_nếu_đúng>, ngược lại thực
hiện <Lệnh_nếu_sai>
if (<Điều_kiện>) {
<Lệnh_nếu_đúng>;
} else {
<Lệnh_nếu_sai>;
}
If là một cấu trúc điều khiển được sử dụng phổ biến nhất. Đoạn chương trình sau sẽ nêu ví dụ sử
dụng lệnh if.
9
Lập trình C++ Nguyễn Phú Quảng
#include <iostream>
#include <stdlib.h>
using namespace std;
int main ()
{
int gio;
cout << "Nhap vao gio (0-24): ";
cin >> gio;
if (gio<12)
{
cout << "Chao buoi sang";
} else
{
cout << "Chao buoi chieu";
}
getchar();
getchar();
}
10
Lập trình C++ Nguyễn Phú Quảng
int main ()
{
for (int i=0; i<10; i++)
{
for (int j=0; j<10; j++) cout << setw(4) << i*j;
cout << endl;
}
getchar();
}
11
Lập trình C++ Nguyễn Phú Quảng
Vòng lặp for của C++ có thể thay thế vòng lặp for... downto của Pascal bằng cách thay đổi điều kiện
kiểm tra và lệnh tăng thành lệnh giảm. Ví dụ sau viết ra 100 số từ 990, 980, 970 đến 0
#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int main ()
{
for (int i=990; i>=0; i-=10)
{
cout << i << " ";
}
getchar();
}
int main()
{
int n = 100; // Viết tất cả các số nguyên tố từ 1 đến n ra màn hình
int i, j, m;
for (i=1; i<=n; i++) { // Xét từng số
// Xét tất cả các số từ 2 đến căn 2 của i
for (j=2, m = sqrt(i); j<=m; j++) {
if (i%j==0) break; // Nếu có 1 số là ước thì thoát
}
// Nếu thoát khi i chia hết cho j thì i ko phải là số nguyên tố
// Nếu i không chia hết cho j, số i là nguyên tố
if (i%j!=0) cout << i << " ";
}
getchar();
}
12
Lập trình C++ Nguyễn Phú Quảng
int main()
{
int n = 100;
int i, j, m;
for (i=1; i<=n; i++) {
for (j=2, m = sqrt(i); j<=m; j++) {
// Nếu i chia hết cho j, nhảy đến vị trí "KoNguyenTo"
if (i%j==0) goto KoNguyenTo;
}
// Nếu i không chia hết cho bất cứ j nào, i là số nguyên tố
cout << i << " ";
// Nếu i chia hết cho 1 số j, Chương trình sẽ nhảy trực tiếp đến
// vị trí KoNguyenTo
KoNguyenTo:;
}
getchar();
}
IV. HÀM
IV.1. Định nghĩa hàm
Cú pháp:
<Kiểu_hàm> <Tên_hàm>([Danh_sách_tham_số])
{
<Lệnh>;
return <Giá_trị_trả_về>;
<Lệnh>;
}
Trong đó:
- <Kiểu_hàm>: Kiểu giá trị trả về của hàm, (nếu hàm không trả về giá trị, kiểu hàm là void)
13
Lập trình C++ Nguyễn Phú Quảng
- <Tên_hàm>: Tên của hàm (quy ước giống như đặt tên biến – không có dấu cách, không
chứa ký tự đặc biệt)
- Nếu hàm có kiểu void, có thể dùng lệnh return để thoát khỏi hàm
- Nếu hàm có kiểu khác void, bạn phải trả về giá trị tương ứng với kiểu (ví dụ kiểu int thì
phải là return 1)
- Nếu không có tham số, bạn vẫn phải có cặp ngoặc ()
Danh sách tham số được liệt kê như sau:
<Kiểu_tham_số_1> <Tên_tham_số_1>, <Kiểu_tham_số_2> <Tên_tham_số_2>,...
int main()
{
cout << "Ham tinh tong: " << Tong(20,34) << endl;
getchar();
}
14
Lập trình C++ Nguyễn Phú Quảng
- Sau khi thoát khỏi hàm, vùng bộ nhớ cấp cho các tham số và biến trong hàm được khôi
phục, stack trở về trạng thái cũ
- Các hàm có thể gọi lẫn nhau, mỗi lần gọi hàm stack lại đầy thêm. Stack chứa thông tin thứ
tự các hàm gọi nhau và các biến trong hàm đó (thông tin này gọi là Call stack)
int main()
{
int n;
cout << "n = "; cin >> n;
cout << "n! = " << GiaiThua(n) << endl;
fflush(stdin);
getchar();
}
Chương trình trên tính giai thưa của một số bằng cách sử dụng hàm GiaiThua. Bản thân hàm này sử
dụng công thức truy hồi n! = n * (n-1)! Hàm GiaiThua(n) gọi hàm GiaiThua(n-1), GiaiThua(n-1)
gọi GiaiThua(n-2). Cứ tuần tự cho đến hàm GiaiThua(1), hàm này quá đơn giản vì cho giá trị 1 nên
không gọi đệ quy.
Như vậy, hàm GiaiThua(n) được tính bằng cách gọi đệ quy n lần thay vì dùng vòng lặp. Để các bạn
hiểu rõ hơn về đệ quy, chúng ta phân tích hoạt động của hàm GiaiThua(n) với n=4.
Begin End
return
4*6=24 return
3*2=6
4>=1 4*GiaiThua(3)
Đ
return
2*1=2
3 3*GiaiThua(2)
Đ
2*GiaiThua(1
2>=1 )
Đ
return
1
1>=1
S
Chúng ta hãy thử phân tích quá trình hoạt động của các hàm khi gọi đệ quy qua bảng sau
15
Lập trình C++ Nguyễn Phú Quảng
Bước Mô tả Stack
main() Chương trình bắt đầu hoạt
động từ hàm main()
gọi GiaiThua(4) Hàm main() thực hiện lời gọi
hàm GiaiThua(4) 4
16
Lập trình C++ Nguyễn Phú Quảng
V. MẢNG
V.1. Định nghĩa
VII. STRUCT
VII.1. Định nghĩa Struct
17
Lập trình C++ Nguyễn Phú Quảng
VIII. Stream
VIII.1. File stream
int main () {
ofstream myfile;
myfile.open ("c:/vidu.txt");
myfile << "Ghi thong tin ra file." << endl;
myfile << 123.43 << endl;
myfile.close();
return 0;
}
int main () {
ifstream myfile;
myfile.open ("c:/vidu.txt");
int a, b, c, d, e;
myfile >> a >> b >> c >> d >> e;
myfile.close();
cout << a << " " << b << " " << c << " " << d << " " << e << endl;
getchar();
return 0;
}
Ví dụ trên mới chỉ minh họa đọc các giá trị liên tiếp từ file. Để đọc các dòng, xâu ký tự... ifstream
có các phương thức sau:
- f.eof(): Kiểm tra xem đã hết file chưa? Trả về 0 nếu chưa hết file, 1 nếu đã hết file
18
Lập trình C++ Nguyễn Phú Quảng
- f.get(): Đọc 1 ký tự trên file: Sử dụng để đọc ký tự xuống dòng sau khi dùng các toán tử dẫn
hướng từ stream ra biến (tham khảo ví dụ). Trả về ký tự vừa đọc được.
- getline(f, str): Đọc một dòng từ file (vào xâu ký tự str)
- Sau khi đọc 1 dòng từ file, bạn có thể dùng istringstream để đọc dữ liệu từ luồng xâu ký tự
vào các biến. (tham khảo ví dụ)
/*
Đọc vào từ file readfile.txt với cấu trúc
Dòng 1: Số dòng của mảng 2 chiều
Các dòng tiếp theo, mỗi dòng là các phần tử của mảng 2 chiều (lưu ý, số phần tử
mỗi dòng có thể khác nhau, không có số ghi số lượng phần tử ở đầu)
*/
#include <iostream>
#include <fstream> // header cho đọc ghi file
#include <sstream> // header cho đọc ghi trên string
#include <stdio.h>
#include <vector>
using namespace std;
int main () {
int n;
typedef vector<int> dong; // Kiểu dòng (mảng số nguyên)
vector<dong> a; // Mảng 2 chiều (mảng các dòng)
string line;
int b;
getchar();
}
VIII.2.1. istringstream
VIII.2.2. ostringstream
19
Lập trình C++ Nguyễn Phú Quảng
C++ cũng cho phép làm tương tự với thư viện iomanip (viết tắt của manipulate). Để sử dụng thư
viện này, bạn khai báo
#include <iomanip>
int main () {
cout << setprecision(4) << (20.0/3) << endl; // Ghi ra 3 chữ số sau dấu phẩy 6.667
getchar();
}
int main () {
cout << setw(10) << 123 << setw(10) << 456 << endl; // 123 456
cout << setw(10) << 123 << 456 << endl; // 123456
cout << left << setw(10) << 123 << setw(10) << 456 << endl; // 123 456
getchar();
}
int main () {
cout << setbase(16) << 123 << endl; // 7b
cout << 123 << endl; // 7b
cout << setbase(8) << 123 << endl; // 173
cout << showbase;
cout << setbase(16) << 123 << endl; // 0x7b
cout << setbase(10) << 123 << endl; // 123
cout << setbase(8) << 123 << endl; // 0173
cout << noshowbase;
cout << setbase(16) << 123 << endl; // 7b
getchar();
}
20
Lập trình C++ Nguyễn Phú Quảng
XI. LỚP
XII. MẪU
XIII.1.1. Container
Container (thùng chứa) là khái niệm chỉ các đối tượng lưu trữ các đối tượng (giá trị) khác. Đối
tượng container sẽ cung cấp các phương thức để truy cập các thành phần (element) của nó. Cụ thể
hơn, tất cả các container đều chứa các bộ lặp (iterator) để cho phép duyệt qua toàn bộ các element
của container.
Các container được phân loại theo tính chất thứ tự của các element, bao gồm các loại sau:
- Forward container
- Reversible container
- Random Access container
Một số container hay được sử dụng nhất gồm vector (tương tự như mảng), vector là Random access
container (người dùng có thể truy cập trực tiếp bất cứ phần tử nào trên vector).
XIII.1.2. Iterator
Iterator (bộ lặp) là khái niệm sử dụng để chỉ một con trỏ trỏ đến các đối tượng. Mỗi container có
một loại iterator khác nhau để trỏ đến các thành phần của container. Để iterator lần lượt trỏ đến từng
thành phần trong container, chúng ta sử dụng các toán tử tăng (++).
21
Lập trình C++ Nguyễn Phú Quảng
Khái niệm iterator cho phép bạn làm việc một cách tổng quát với bất kỳ một kiểu dữ liệu nào, từ
những kiểu dữ liệu truy cập ngẫu nhiên (vector) đến các ánh xạ (map), tập hợp (set), danh sách (list)
cho đến những kiểu dữ liệu đơn giản như mảng.
Do đó, iterator gắn liền với tất cả các loại container, đây là khái niệm bạn cần nắm rất vững nếu
muốn làm việc tốt với STL.
Phương thức Mô tả
a.begin() Trả về iterator bắt đầu của container
a.end() Trả về iterator cuối cùng của container
a.size() Kích thước (số lượng element) của container
a.max_size() Kích thước tối đa của container
a.empty() Trả về giá trị != 0 nếu container trống (không có element nào), 0 nếu
ngược lại
a.swap(b) Hoán đổi 2 container với nhau (giống việc hoán đổi giá trị của 2 biến
kiểu số)
Kiểu vector có thể coi là kiểu mảng trong lập trình C truyền thống. Mảng là tập hợp các giá trị cùng
kiểu, được sắp xếp nối tiếp nhau. Các phần tử của mảng có thể được truy cập ngẫu nhiên qua chỉ số.
Vấn đề đặt ra: Nếu vector là mảng thì tại sao lại phải sử dụng vector khi bạn đã quá quen thuộc với
mảng? Chúng tôi xin phân tích một số nhược điểm sau của mảng:
- Nếu bạn sử dụng mảng tĩnh: Mảng này luôn được khai báo với kích thước tối đa mà bạn có
thể dùng đến → tốn nhiều vùng nhớ thừa
- Nếu bạn sử dụng mảng động: Bạn phải xin cấp phát bộ nhớ, làm việc với con trỏ. Con trỏ là
khái niệm hay trong C, C++, nhưng nó là nguyên nhân của rất nhiều rắc rối trong lập trình.
- Nhược điểm quan trọng nhất: Nếu bạn sử dụng mảng vượt chỉ số vượt quá kích thước đã
khai báo, C++ sẽ không thông báo lỗi, điều này dẫn đến lỗi dây chuyền do các lệnh lỗi đã
tác động đến các biến khác trong chương trình (trong Pascal bạn có thể kiểm tra tràn chỉ số
mảng bằng dẫn biên dịch range check).
vector là một container cung cấp khả năng sử dụng mảng mềm dẻo, có kiểm soát range check khi
cần thiết, với kích thước tùy ý (mà không cần phải sử dụng con trỏ). Ngoài ra vector cho phép bạn
chèn thêm hoặc xóa đi một số phần tử chỉ bằng 1 lệnh (không phải sử dụng vòng lặp như đối với
mảng).
Để sử dụng vector, bạn phải khai báo file header với cú pháp
#include <vector>
22
Lập trình C++ Nguyễn Phú Quảng
XIII.2.1. Ví dụ 1
Trong đó, bạn có thể khai báo kiểu là bất cứ kiểu biến nào. Để hiểu rõ hơn về vector, bạn hãy theo
dõi ví dụ sau
#include <iostream> // Thư viện iostream phục vụ ghi dữ liệu ra màn hình
#include <vector> // Thư viện vector, sử dụng kiểu vector
#include <stdio.h> // Thư viện stdio (sử dụng hàm getchar() để dừng ct)
using namespace std; // Sử dụng namespace std
int main() {
vector<int> V; // V kiểu vector số nguyên (sử dụng giống mảng int)
V.resize(3); // Đặt kích thước của biến V là 3 (giống mảng 3 pt)
V[0] = 5; // Gán giá trị cho các phần tử của biến V
V[1] = 6; // Sử dụng dấu móc [] hoàn toàn giống với mảng
V[2] = 7;
for (int i=0; i<V.size(); i++) { // Ghi giá trị các phần tử của V ra màn hình
cout << V[i] << endl; // Nếu sử dụng mảng, bạn phải có biến lưu kích
} // thước, còn vector có hàm cho biến kích thước
getchar(); // Dừng chương trình để xem kết quả
}
Ví dụ trên cho bạn thấy việc sử dụng vector rất đơn giản, hoàn toàn giống với mảng nhưng bộ nhớ
được quản lý tự động, bạn không phải quan tâm đến giải phóng các vùng bộ nhớ đã xin cấp phát.
XIII.2.2. Ví dụ 2
Tuy nhiên, việc sử dụng vector không chỉ dừng lại ở những ưu điểm trên, chúng ta hãy nghiên cứu
ví dụ tiếp theo để thấy được các ưu điểm khác của vector.
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
int i;
vector<int> V;
for (i=0; i<5; i++) // Lặp 5 lần, mỗi lần đưa thêm 1 số vào vector
V.push_back(i); // Như vậy, vector có thể được sử dụng như stack
cout << endl << "Mang truoc khi insert" << endl;
for (i=0; i<V.size(); i++) // Ghi lại nội dung của mảng ra màn hình
cout << V[i] << endl;
V.insert( V.begin()+3, 100 ); // Chèn vào vị trí thứ 3 của vector giá trị 100
cout << endl << "Mang sau khi insert" << endl;
for (i=0; i<V.size(); i++) // In nội dung của vector sau khi chèn
cout << V[i] << endl; // vector sẽ có 6 phần tử, phần tử 100 chèn vào
// Vị trí thứ 3 của vector
V.erase( V.begin()+3 ); // Xóa phần tử vừa chèn vào đi
cout << endl << "Mang sau khi erase" << endl;
for (i=0; i<V.size(); i++) // In nội dung của vector sau khi xóa
cout << V[i] << endl; // Vector lại giống như lúc mới khởi tạo
getchar();
}
Với ví dụ trên, bạn có thể thấy những ưu điểm sau của vector
- Bạn có thể sử dụng vector như 1 stack. Thao tác push_back() của vector cho phép bạn thêm
1 giá trị mới vào vector. Thao tác này sẽ tự động tăng kích thước của vector, do đó bạn sẽ
không phải quan tâm đến quản lý kích thước của vector.
- vector cho phép bạn chèn thêm 1 phần tử vào các vị trí bất kỳ của vector, các phần tử nằm
sau vị trí này sẽ được dịch đi 1 vị trí đển lấy khoảng trống cho phần tử mới. Thao tác chèn
phần tử có thể thực hiện dễ dàng đối với mảng thường bằng cách sử dụng vòng lặp để
chuyển dịch các phần tử. Tuy nhiên, để chương trình hoạt động đúng trong mọi trường hợp,
bạn phải tính đến nhiều trường hợp: dung lượng hiện tại của mảng có đủ không? Nếu không
đủ phải xin cấp phát thêm... Nếu sử dụng vector, các bạn sẽ dễ dàng thực hiện chức năng
này chỉ bằng 1 lệnh và luôn đảm bảo rằng lệnh này sẽ hoạt động chính xác.
23
Lập trình C++ Nguyễn Phú Quảng
- Tương tự với thao tác xóa một phần tử khỏi vector, bạn cũng chỉ cần sử dụng 1 lệnh.
XIII.2.3. Ví dụ 3
Chúng ta tiếp tục theo dõi ví dụ tiếp theo để tìm hiểu tiếp các khả năng của vector
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
// Mảng các xâu ký tự (dữ liệu test)
char *Chao[] = {"Xin", "chao", "tat", "ca", "cac", "ban"};
int n = sizeof(Chao)/sizeof(*Chao); // Tính kích thước của mảng
// Cách tính này sử dụng một mẹo:
// Kích thước của toàn bộ mảng chia cho
// Kích thước của 1 phần tử của mảng
vector<char *> V; // Vector chứa các xâu ký tự
// (cũng giống mảng, vector có thể chứa
// mọi loại giá trị)
int i;
cout << endl << "vector truoc khi xoa" << endl;
for (i=0; i<n; i++) {
V.push_back(Chao[i]); // Đưa các giá trị từ mảng vào vector
}
cout << endl << "vector sau khi xoa" << endl;
for (i=0; i<V.size(); i++) {
cout << V[i] << endl; // In ra các giá trị của vector
}
Như vậy, chúng ta biết thêm một số chức năng của vector
- Vector không chỉ làm việc với số mà còn có thể làm việc với tất cả các dạng dữ liệu khác
(đây là đặc điểm của lập trình khái quát với template)
- Vector có thể xóa nhiều thành phần một lúc
- Vector có thế xóa tất cả các thành phần bằng phương thức clear().
24
Lập trình C++ Nguyễn Phú Quảng
XIII.2.4. Ví dụ 4
Còn một vấn đề đã được đề cập đến từ khi chúng tôi giới thiệu về vector, đó là khả năng kiểm tra
tràn chỉ số mảng (range check), để biết về khả năng này, chúng ta lại tiếp tục với một ví dụ mới
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
try { // sử dụng try...catch để bẫy lỗi
vector<long> V(3, 10); // Khởi tạo vector gồm 3 thành phần
// Tất cả gán giá trị 10
cout << "V[0]=" << V[0] << endl; // Đưa thành phần 0 ra màn hình
cout << "V[1]=" << V[1] << endl; // Đưa thành phần 1 ra màn hình
cout << "V[2]=" << V[2] << endl; // Đưa thành phần 2 ra màn hình
cout << "V[3]=" << V[3] << endl; // Thành phần 3 (lệnh này hoạt động không
// đúng vì V chỉ có 3 thành phần 0,1,2
cout << "V[4]=" << V[4] << endl; // Thành phần 4 (càng không đúng)
// Nhưng 2 lệnh trên đều không gây lỗi
cout << "V[0]=" << V.at(0) << endl; // Không sử dụng [], dùng phương thức at
cout << "V[1]=" << V.at(1) << endl; // Thành phần 1, OK
cout << "V[2]=" << V.at(2) << endl; // Thành phần 2, OK
cout << "V[3]=" << V.at(3) << endl; // Thành phần 3: Lỗi, chương trình dừng
cout << "V[4]=" << V.at(4) << endl;
getchar();
} catch (exception &e) {
cout << "Co loi xay ra" << endl; // Dòng thông báo lỗi (nếu chương trình
getchar(); // phát sinh lỗi khi chạy
}
}
XIII.2.5. Ví dụ 5
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
typedef vector<long> vlong;
vector<vlong> V(3, vlong(3) );
int i, j;
for (i=0; i<3; i++)
25
Lập trình C++ Nguyễn Phú Quảng
Ví dụ này minh họa việc sử dụng mảng 2 chiều, thực chất đây là một vector của vector. Mảng 2
chiều sử dụng biện pháp này có thể có kích thước khác nhau giữa các dòng (ví dụ mảng 2 chiều là
nửa trên của ma trận)
int main()
{
chiso.resize( 5 );
chiso[5] = 10;
}
/*
5
Duc
Anh
Van
Thu
Ba
*/
26
Lập trình C++ Nguyễn Phú Quảng
int main ()
{
string str="day la .. xau thu";
string istr = "them";
str.insert(8, istr);
cout << str << endl;
getchar();
}
int main ()
{
string str="day cung la xau thu";
str.erase(0, 3); // " cung la xau thu"
cout << str << endl;
str.erase(6, 2);
cout << str << endl; // " cung xau thu"
getchar();
}
int main ()
{
string str="con cho la con cho con. Con meo ko phai la con cho";
str.replace(4, 3, "CHO"); // "con CHO la con cho con. Con meo ko phai la con cho";
cout << str << endl;
getchar();
}
int main ()
{
string str="ConCho chay qua rao";
string str2 = string(str.begin()+0, str.begin()+3); // "Con"
cout << str2 << endl;
str2 = string(str.begin()+7, str.begin()+11); // "chay"
cout << str2 << endl;
getchar();
}
27
Lập trình C++ Nguyễn Phú Quảng
int main ()
{
string str="ConCho chay qua rao";
cout << str.find("chay") << endl; // 7
cout << (int)str.find("Chay") << endl; // -1
getchar();
}
int main ()
{
string str="ConCho chay qua chay qua rao";
cout << str.find("chay") << endl; // 7
cout << (int)str.rfind("chay") << endl; // 16
getchar();
}
XIII.3.7. Ví dụ 1
Để làm việc với string của STL, chúng ta cùng xem xét ví dụ sau
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
int main() {
string s = "Hello string"; // Khai báo biến kiểu string
cout << "Noi dung string: " << s << endl; // In nôi dung string ra màn hình
cout << "Chieu dai cua string: " << s.size() << endl;
// Chiều dài
cout << "Ky tu 0: " << s[0] << endl; // In ký tự đầu tiên của xâu
cout << "Ky tu 1: " << s[1] << endl; // In ký tự thứ 2
cout << "Ky tu 2: " << s[2] << endl; // In ký tự thứ 3
s += " co the noi string"; // Nối xâu ký tự
cout << "Noi dung string sau khi noi: " << s << endl;
s.erase(s.begin()+5, s.begin()+5+7); // Xóa 1 đoạn trên xâu ký tự
cout << "Noi dung string sau khi xoa: " << s << endl;
getchar();
}
28
Lập trình C++ Nguyễn Phú Quảng
#include <stdio.h>
#include <string>
list là kiểu danh sách liên kết 2 chiều (hãy liên hệ với các kiến thức của môn Cấu trúc dữ liệu và
Giải thuật). Khác biệt của kiểu danh sách liên kết với kiểu mảng ở chỗ:
- Mảng cần nhiều thời gian để thêm hoặc xóa phần tử (dịch chuyển các phần tử phía sau sang
phải hoặc sang trái), còn danh sách liên kết thực hiện các thao tác thêm, xóa với thời gian
không đáng kể (chỉ cần cắt đứt liên kết cũ, tạo ra liên kết mới).
- Mảng cho phép truy xuất các phần tử ngay lập tức thông qua chỉ số (truy cập ngẫu nhiên -
random access), còn danh sách liên kết phải truy cập các phần tử theo thứ tự tuần tự.
29
Lập trình C++ Nguyễn Phú Quảng
Để sử dụng list, bạn phải khai báo file header list với cú pháp sau
#include <list>
Phương thức Mô tả
c1 != c2 Trả về 1 nếu hai danh sách khác nhau (nhanh hơn !(c1==c2))
c1.merge(c2) Trộn danh sách c1 với danh sách c2 (với điều kiẹn cả 2 danh sách đều đã
sắp xếp với toán tử <). Danh sách được trộn cũng sẽ được sắp xếp với
toán tử <. Đây là thuật toán trộn 2 đường với 2 danh sách đã được sắp
xếp (độ phức tạp là tuyến tính)
list<int>::iterator vi;
cout << endl << "Danh sach theo chieu xuoi" << endl;
for (vi=V.begin(); vi!=V.end(); vi++) {
cout << *vi << endl;
}
list<int>::reverse_iterator rvi;
cout << endl << "Danh sach theo chieu nguoc" << endl;
for (rvi=V.rbegin(); rvi!=V.rend(); rvi++) {
cout << *rvi << endl;
}
getchar();
}
30
Lập trình C++ Nguyễn Phú Quảng
int main() {
int A[] = {1,2,3,4,5};
int n = sizeof(A) / sizeof(*A);
list<int> V(A, A+n);
list<int>::iterator i;
cout << endl << "Danh sach ban dau" << endl;
for_each( V.begin(), V.end(), printint );
cout << endl << "Danh sach sau khi chen vao cuoi" << endl;
V.push_back(100);
for_each( V.begin(), V.end(), printint );
cout << endl << "Danh sach sau khi chen vao dau" << endl;
V.push_front(200);
for_each( V.begin(), V.end(), printint );
getchar();
}
V.sort();
list<int>::iterator vi;
cout << endl << "Danh sach sau khi sap xep" << endl;
for (vi=V.begin(); vi!=V.end(); vi++) {
cout << *vi << endl;
}
getchar();
}
31
Lập trình C++ Nguyễn Phú Quảng
list<int>::iterator vi;
cout << endl << "Danh sach A sau khi sap xep" << endl;
Va.sort();
for (vi=Va.begin(); vi!=Va.end(); vi++) {
cout << *vi << endl;
}
cout << endl << "Danh sach B sau khi sap xep" << endl;
Vb.sort();
for (vi=Vb.begin(); vi!=Vb.end(); vi++) {
cout << *vi << endl;
}
cout << endl << "Danh sach A sau khi tron voi danh sach B" << endl;
Va.merge(Vb);
for (vi=Va.begin(); vi!=Va.end(); vi++) {
cout << *vi << endl;
}
getchar();
}
Kiểu map cho phép bạn lấy tương ứng giữa một giá trị với một giá trị khác, hai giá trị này tạo thành
một cặp giá trị. Trong đó giá trị đầu của cặp là khóa (key), key là duy nhất (không có 2 key cùng
xuất hiện trong 1 map). Do đó, từ key bạn có thể tìm được giá trị tương ứng với key trong cặp.
32
Lập trình C++ Nguyễn Phú Quảng
Nếu thực hiện tìm kiếm bình thường trên một mảng N phần tử, bạn phải mất trung bình N/2 phép
tìm kiếm. Dữ liệu của các key được tổ chức dưới dạng cây heap (lá trái nhỏ hơn gốc, lá phải lớn
hơn gốc) nên việc tìm kiếm các cặp theo khóa rất nhanh, thời gian trung bình là log 2N (vì độ sâu
của cây là log2N).
Nếu bạn biết tận dụng, map có thể ứng dụng để giải rất nhiều dạng bài toán khác nhau. Trong phần
này, chúng tôi xin giới thiệu một số ứng dụng của map
- Sử dụng làm từ điển: Ví dụ: tra cứu thông tin của sinh viên theo mã số
- Đếm số lượng của một thành phần (cho một loạt các phần tử, tìm xem có bao nhiêu phần tử
và mỗi phần tử xuất hiện bao nhiêu lần)
- Sử dụng để lưu ma trận thưa (ma trận thưa là ma trận gồm rất nhiều phần tử bằng 0). Trong
các bài thi Tin học, nhiều trường hợp các bạn gặp phải những ma trận có kích thước rất lớn
(1 triệu x 1 triệu chẳng hạn) nhưng số lượng phần tử khác 0 chỉ khoảng vài trăm nghìn.
Trường hợp này sử dụng map để lưu ma trận là rất thích hợp.
c[k] Trả về giá trị tương ứng với khóa k (chú ý: nếu chưa có cặp nào có khóa
k, map sẽ tự tạo một cặp mới có khóa k và gán giá trị mặc định. Đối với
kiểu số, giá trị mặc định là 0)
c.find(k) Trả về iterator tương ứng với khóa k, nếu không tìm thấy thì trả về end()
map<int, int> m;
for (int i=0; i<n; i++) {
m[a[i]] ++;
}
map<int, int>::iterator j;
for (j=m.begin(); j!=m.end(); j++)
cout << "Phan tu " << j->first << " xuat hien "
<< j->second << " lan " << endl;
getchar();
}
33
Lập trình C++ Nguyễn Phú Quảng
class sv {
public:
int maso;
string ten;
string lop;
void print() {
cout << "****************************************" << endl
<< "Ma so: " << maso << endl
<< "Ho ten: " << ten << endl
<< "Lop: " << lop << endl;
}
};
int main () {
sv a[] = {
{1342, "Mai", "48th"},
{43212, "Lan", "47th"},
{33133, "Cuc", "47th"},
{43321, "Truc", "45th"},
{1234, "Dao", "42th"}};
int n = sizeof(a)/sizeof(a[0]);
map<int, sv> m;
for (int i=0; i<n; i++) m[a[i].maso] = a[i];
m[43212].print();
m[1234].print();
getchar();
return 0;
}
map<string, int> m;
for (int i=0; i<n; i++) {
m[a[i]] ++;
}
map<string, int>::iterator j;
for (j=m.begin(); j!=m.end(); j++)
cout << "Xau ky tu '" << j->first << "' xuat hien "
<< j->second << " lan " << endl;
getchar();
}
34
Lập trình C++ Nguyễn Phú Quảng
Do thư viện <algorithm> gồm rất nhiều hàm khác nhau, người ta phân thành các nhóm hàm sau:
- Nhóm các hàm không thay đổi giá trị của container
- Nhóm các hàm thay đổi giá trị của container
- Nhóm các hàm sắp xếp
- Nhóm các hàm trên danh sách được sắp xếp
- Nhóm các hàm trộn
- Nhóm các làm trên heap
- Nhóm các hàm tìm min/max
XIII.9.1. Nhóm các hàm không thay đổi giá trị của container
#include <iostream>
35
Lập trình C++ Nguyễn Phú Quảng
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
cout << "So luong so 1 trong mang: " << count(A, A+N, 1) << endl;
cout << "So luong so 3 trong mang: " << count(A, A+N, 3) << endl;
cout << "So nho nhat trong mang: " << *min_element(A, A+N) << endl;
cout << "So lon nhat trong mang: " << *max_element(A, A+N) << endl;
getchar();
return 0;
}
XIII.9.2. Nhóm các hàm thay đổi giá trị của container
int main () {
vector<int> myvector (8); // myvector: 0 0 0 0 0 0 0 0
36
Lập trình C++ Nguyễn Phú Quảng
return 0;
}
int main () {
int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
vector<int> a (myints, myints+8); // 10 20 30 30 20 10 10 20
replace(a.begin(), a.end(), 20, 99); // 10 99 30 30 99 10 10 99
getchar();
}
int main () {
vector<int> a;
37
Lập trình C++ Nguyễn Phú Quảng
#include <stdlib.h>
using namespace std;
int main () {
vector<int> a;
#include <iostream>
#include <stdio.h>
#include <algorithm>
cout << "Sap xep danh sach theo chieu tang dan" << endl;
sort(A, A+N);
for (i=0; i<N; i++)
cout << A[i] << endl;
cout << "Sap xep danh sach theo chieu giam dan" << endl;
sort(A, A+N, greater<int>() );
for (i=0; i<N; i++)
cout << A[i] << endl;
getchar();
return 0;
}
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
38
Lập trình C++ Nguyễn Phú Quảng
class sv {
public:
double diem;
char *ten;
bool operator < (const sv &b) const {
return diem < b.diem;
}
};
int main () {
sv a[] = {{6,"Mai"},{7,"Lan"},{5,"Cuc"},{4,"Truc"},{3,"Dao"}};
int n = sizeof(a)/sizeof(*a);
sort(a, a+n);
XIII.9.4. Nhóm các hàm trên danh sách được sắp xếp
Một số thuật toán như tìm kiếm, thêm vào danh sách... hoạt động nhanh hơn (độ phức tạp là log 2n
thay vì n). Thư viện <algorithm> hỗ trợ một số hàm làm việc riêng với các danh sách đã sắp xếp
theo thứ tự tăng dần.
int main () {
int myints[] = {10,20,30,30,20,10,10,20};
int size = sizeof(myints)/sizeof(myints[0]);
vector<int> v(myints,myints+size); // 10 20 30 30 20 10 10 20
vector<int>::iterator low,up;
sort (v.begin(), v.end()); // 10 10 10 20 20 20 30 30
low=lower_bound (v.begin(), v.end(), 20); // ^
up= upper_bound (v.begin(), v.end(), 20); // ^
cout << "lower_bound tro vao vi tri: " << int(low- v.begin()) << endl;
cout << "upper_bound tro vao vi tri: " << int(up - v.begin()) << endl;
getchar();
}
int main () {
int myints[] = {1,2,3,4,5,4,3,2,1};
39
Lập trình C++ Nguyễn Phú Quảng
vector<int> v(myints,myints+9); // 1 2 3 4 5 4 3 2 1
getchar();
}
int main () {
int first[] = {5,10,15,20,25};
int second[] = {50,40,30,20,10};
vector<int> v(10);
vector<int>::iterator it;
sort (first,first+5);
sort (second,second+5);
merge (first,first+5,second,second+5,v.begin());
int main () {
int first[] = {5,10,15,20,25};
int second[] = {50,40,30,20,10};
vector<int> v(10);
vector<int>::iterator it;
sort (first,first+5);
sort (second,second+5);
copy (first,first+5,v.begin());
copy (second,second+5,v.begin()+5);
inplace_merge (v.begin(),v.begin()+5,v.end());
getchar();
}
40
Lập trình C++ Nguyễn Phú Quảng
int main () {
int first[] = {5,10,15,20,25};
int second[] = {50,40,30,20,10};
vector<int> v(10); // 0 0 0 0 0 0 0 0 0 0
vector<int>::iterator it;
sort (first,first+5); // 5 10 10 20 25
sort (second,second+5); // 10 20 30 40 50
cout << "union has " << int(it - v.begin()) << " elements.\n";
getchar();
}
int main () {
int first[] = {5,10,15,20,25};
int second[] = {50,40,30,20,10};
vector<int> v(10); // 0 0 0 0 0 0 0 0 0 0
vector<int>::iterator it;
sort (first,first+5); // 5 10 15 20 25
sort (second,second+5); // 10 20 30 40 50
cout << "difference has " << int(it - v.begin()) << " elements.\n";
getchar();
}
int main () {
int myints[] = {10,20,30,5,15};
vector<int> v(myints,myints+5);
vector<int>::iterator it;
make_heap (v.begin(),v.end());
cout << "initial max heap : " << v.front() << endl;
sort_heap (v.begin(),v.end());
41
Lập trình C++ Nguyễn Phú Quảng
getchar();
}
int main () {
int myints[] = {10,20,30,5,15};
vector<int> v(myints,myints+5);
vector<int>::iterator it;
make_heap (v.begin(),v.end());
cout << "initial max heap : " << v.front() << endl;
sort_heap (v.begin(),v.end());
getchar();
}
Kết quả
initial max heap : 30
max heap after pop : 20
max heap after push: 99
final sorted range : 5 10 15 20 99
int main () {
int myints[] = {10,20,30,5,15};
vector<int> v(myints,myints+5);
vector<int>::iterator it;
make_heap (v.begin(),v.end());
cout << "initial max heap : " << v.front() << endl;
sort_heap (v.begin(),v.end());
return 0;
}
42
Lập trình C++ Nguyễn Phú Quảng
Kết quả
initial max heap : 30
max heap after pop : 20
max heap after push: 99
final sorted range : 5 10 15 20 99
int main () {
int myints[] = {10,20,30,5,15};
vector<int> v(myints,myints+5);
vector<int>::iterator it;
make_heap (v.begin(),v.end());
cout << "initial max heap : " << v.front() << endl;
sort_heap (v.begin(),v.end());
getchar();
}
Kết quả
initial max heap : 30
max heap after pop : 20
max heap after push: 99
final sorted range : 5 10 15 20 99
XIV.1.1. Đề bài
Tên file: SVIEN.*
Dữ liệu vào: từ file B1.INP, gồm:
- Dòng 1: Số sinh viên của trường (n – n<=10000)
- 3*n dòng tiếp theo: Thông tin của 1 sinh viên được ghi trên 3 dòng, bao gồm: Họ tên (tối đa
20 ký tự), lớp (tối đa 6 ký tự), quê quán (tối đa 10 ký tự)
Yêu cầu: Tìm và liệt kê:
- Số lượng lớp trong trường và danh sách các lớp
- Với mỗi lớp:
o Số lượng và danh sách các địa phương là quê quán của sinh viên trong lớp
Dữ liệu ra: tại file B1.OUT với khuôn dạng như sau:
- Dòng 1: Số lớp của trường (k)
- Các dòng tiếp theo là thông tin của từng lớp (mỗi mục được liệt kê dưới đây được trình bày
trên 1 dòng):
43
Lập trình C++ Nguyễn Phú Quảng
o Ten lop
o số sinh viên của lớp, số lượng địa phương (m)
o số lượng sinh viên quê ở địa phương 1, tên địa phương 1
o số lượng sinh viên quê ở địa phương 2, tên địa phương 2
o ...
o số lượng sinh viên quê ở địa phương m, tên địa phương m
B1.INP B1.OUT
5 2
Nguyen Van A 46pm1
46pm1 2 1
Hung Yen 2 Hung Yen
Tran Van B 47th1
47th1 3 2
Hai Duong 2 Hai Duong
Nguyen Thi C 1 Binh Dinh
46pm1
Hung Yen
Vu Van D
47th1
Binh Dinh
Cao Thi E
47th1
Hai Duong
int main() {
ifstream fi;
int n, i;
map<string, int> lop; // map giữa tên lớp và số sinh viên
map<string, map<string, int> > que; // map giữa tên lớp và biến đếm số
// sinh viên của từng quê
// biến đếm này cũng là ánh xạ giữa quê
// và số sinh viên ở quê đó
44
Lập trình C++ Nguyễn Phú Quảng
}
fi.close(); // Đóng file input
ofstream fo;
fo.open(OUTPUT); // Mở file output
map<string, int>::iterator j;
fo << lop.size() << endl; // Ghi số lượng lớp
for (j=lop.begin(); j!=lop.end(); j++) {// Lặp qua từng lớp
fo << j->first << endl; // Ghi tên lớp (thành phần đầu của pair)
map<string, int> &quei = que[j->first];
// Gán biến tham chiếu đến biến đếm quê
// Của lớp tương ứng
fo << j->second << " " << quei.size() << endl;
// Ghi số lượng sinh viên của lớp và
// số lượng quê quán của sv trong lớp
map<string, int>::iterator t;
for (t=quei.begin(); t!=quei.end(); t++) {
// Với mỗi vùng quê, ghi số lượng sv
// ở quê đó và tên quê
fo << t->second << " " << t->first << endl;
}
}
fo.close(); // Đóng file output
}
XIV.2.1. Đề bài
Tên file: RAO.*
- Một mảnh đất hình chữ nhật (kích thước tối đa 32000x32000) được rào bằng các tường rào
song song với các cạnh của hình chữ nhật
- Một khoảng đất được gọi là được bảo vệ nếu nó được giới hạn bởi các tường rào
- Hãy tìm khoảng đất có diện tích lớn nhất được bảo vệ
Dữ liệu vào: từ file RAO.INP
- Dòng đầu: 2 số nguyên w h là chiều rộng và chiều cao của mảnh đất
- Dòng thứ 2: số dòng lượng tường rào (n<=100)
- n dòng tiếp theo: mỗi dòng gồm 4 số nguyên (cách nhau bởi dấu cách) là tọa độ 2 điểm
đầu và cuối của rào. Dữ liệu vào đảm bảo là rào song song với cạnh của hình chữ nhật
Dữ liệu ra: tại file B2.OUT, gồm:
- Dòng 1: Diện tích của vùng được bảo vệ có diện tích lớn nhất, nếu không có thì in ra giá trị -1
45
Lập trình C++ Nguyễn Phú Quảng
XIV.3. Robot
XIV.3.1. Đề bài
Bài 3 (Robot). Tên file: ROBOT.*
Một con robot di chuyển trên một lưới ô vuông bằng cách nhận một trong các lệnh sau:
R: Quay phải 45 độ
L: Quay trái 45 độ
G n: Tiến về phía trước 1 khoảng n (n < 1000)
U: Nhấc chổi lên
D: Hạ chổi xuống
(chú ý rằng khi hạ chổi xuống là ô đó đã được tô màu)
Trạng thái ban đầu của robot là nhấc chổi, tọa độ ban đầu là điểm (0, 0)
Input: Nhạp dữ liệu từ file input.txt gồm các thông tin sau
Mỗi dòng là 1 trong 5 lệnh kể trên (tối đa 1000 lệnh)
Output: Kết quả ghi ra file output.txt
Dòng đầu tiên là 1 số nguyên chỉ ra số lượng ô được tô màu
int main() {
ifstream fi;
fi.open(INPUT);
string s;
int huong = 0; // Hướng khởi đầu
int dx[] = {1,0,-1,0}; // Vector di chuyển tương ứng theo hướng
int dy[] = {0,-1,0,1};
int n, i, x=0, y=0; // Tọa độ đầu tiên
bool choi = false; // Trạng thái của chổi
46
Lập trình C++ Nguyễn Phú Quảng
ofstream fo;
fo.open(OUTPUT);
fo << A.size();
fo.close();
}
XIV.4. Dijsktra
XIV.4.1. Đề bài
XIV.5.1. Đề bài
Cho n hình chữ nhật có cạnh song song với các trục Ox, Oy. Tìm tổng diện tích không gian bị che
bởi các hình chữ nhật này (Diện tích phần hợp của các hình chữ nhật);
47
Lập trình C++ Nguyễn Phú Quảng
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <algorithm>
int n;
vector<dong> a; // Bảng đánh dấu các ô bị che
vector<chunhat> cn; // Danh sách các hình chữ nhật
vector<double> tx, ty; // Tọa độ các lưới dọc và ngang
inline void swap(double &a, double &b) // Hàm đổi chỗ 2 số thực
{
double t = a; a = b; b = t; // Hàm khai báo inline để chạy nhanh hơn
}
int main()
{
fstream f(input);
f >> n; f.get(); // Số hình chữ nhật
cn.resize(n);
for (int i=0; i<n; i++) // Đọc thông tin từng hình chữ nhật
{
chunhat &a = cn[i]; // Đổi chỗ nếu các tọa độ không hợp lệ
f >> a.xmin >> a.ymin >> a.xmax >> a.ymax; f.get();
if (a.xmin>a.xmax) swap(a.xmin, a.xmax);
if (a.ymin>a.ymax) swap(a.ymin, a.ymax);
f.close();
map<double, int> mx, my; // Bảng ánh xạ giữa tọa độ (số thực)
for (int i=0; i<2*n; i++) // và số thứ tự
{ // của tọa độ đó trên lưới (số nguyên)
mx[tx[i]] = i;
my[ty[i]] = i;
}
48
Lập trình C++ Nguyễn Phú Quảng
for (int x=xmin; x<xmax; x++) // Duyệt qua tất cả các thuộc hcnhật
for (int y=ymin; y<ymax; y++) {
if (a[x][y]==0) // Nếu ô chưa được đánh dấu
{
a[x][y] = 1; // Đánh dấu ô đó, và tính thêm diện tích
dt += (tx[x+1]-tx[x])*(ty[y+1]-ty[y]);
}
}
}
ofstream fo;
fo.open(output);
fo << dt; // Tổng diện tích của các hình chữ nhật
fo.close();
}
/*
2
0 0 1 1
0 0 2 2
*/
49