CẤU TRÚC DỮ LIỆU PAIR TRONG C++

Bài giảng được biên soạn lại rõ ràng, hiện đại, có mục lục liên kết, ví dụ minh họa và bài tập luyện tập dùng trực tiếp trong OJ.

C++ STL pair<T1,T2> first / second sort pair Bài tập OJ

1. Pair là gì?

Pair là một cấu trúc dữ liệu trong thư viện chuẩn C++ dùng để gom hai giá trị thành một đối tượng duy nhất.

Mỗi đối tượng pair có hai thành phần: firstsecond.
Thành phầnÝ nghĩaVí dụ
firstGiá trị thứ nhất của cặpp.first
secondGiá trị thứ hai của cặpp.second

Ví dụ trong hình học, một điểm trên mặt phẳng Oxy có thể lưu bằng hai số xy. Khi đó ta có thể dùng pair<int,int> để lưu tọa độ.

2. Cú pháp khai báo pair

Cú pháp tổng quát:

pair<kiểu_dữ_liệu_1, kiểu_dữ_liệu_2> tên_pair;

Một vài ví dụ:

pair<int, int> p;
pair<string, int> hs;
pair<int, double> diem;

So sánh cách dùng struct và pair

Dùng struct

struct toado {
    int x; // hoành độ
    int y; // tung độ
};

toado p;

Dùng pair

pair<int, int> p;

// quy ước:
// p.first  là hoành độ x
// p.second là tung độ y
Nhận xét: struct dễ hiểu hơn khi dữ liệu có nhiều thuộc tính rõ tên. pair ngắn gọn hơn khi chỉ cần lưu đúng 2 giá trị.

3. Truy cập dữ liệu bằng first và second

Muốn lấy hoặc gán giá trị cho hai thành phần của pair, ta dùng firstsecond.

#include<bits/stdc++.h>
using namespace std;

int main() {
    pair<int, int> p;

    cin >> p.first >> p.second;
    cout << p.first << " " << p.second;

    return 0;
}
InputÝ nghĩaOutput
3 5p.first = 3, p.second = 53 5

4. Khởi tạo pair và hàm make_pair()

Cách 1: Khởi tạo trực tiếp

pair<int, char> p(1, 'a');

cout << p.first;   // 1
cout << p.second;  // a

Cách 2: Gán bằng make_pair()

pair<int, int> p;
p = make_pair(1, 2);

cout << p.first << " " << p.second; // 1 2

Cách 3: Gán bằng dấu ngoặc nhọn

pair<string, int> hs;
hs = {"AN", 10};

cout << hs.first << " " << hs.second; // AN 10
Lưu ý: Nếu pair là biến toàn cục thì các kiểu số thường được khởi tạo bằng 0, string rỗng. Nếu khai báo trong hàm, nên chủ động gán giá trị trước khi dùng.

5. Các phép so sánh pair

Pair có thể dùng trực tiếp các toán tử so sánh:

== != < > <= >=

C++ so sánh pair theo thứ tự từ trái sang phải: so sánh first trước, nếu first bằng nhau thì mới so sánh second.
p1p2Kết quảGiải thích
(1,20)(2,10)p1 < p2 đúng1 < 2
(1,20)(1,18)p1 > p2 đúngfirst bằng nhau, so sánh second: 20 > 18
(1,20)(1,20)p1 == p2 đúngCả first và second đều bằng nhau
pair<int, int> a = {1, 20};
pair<int, int> b = {1, 18};

if (a > b)
    cout << "a lon hon b";

6. swap() và sort() với pair

Đổi chỗ hai pair

pair<int, int> p1, p2;
p1 = {1, 2};
p2 = {5, 1};

swap(p1, p2);

cout << p1.first << " " << p1.second << '\n'; // 5 1
cout << p2.first << " " << p2.second << '\n'; // 1 2

Sắp xếp mảng pair

Nếu dùng sort(a, a+n), C++ sẽ tự sắp xếp tăng dần theo first, nếu first bằng nhau thì tăng dần theo second.

#include<bits/stdc++.h>
using namespace std;

int main() {
    pair<int, int> a[6];

    a[1] = make_pair(1, 2);
    a[2] = make_pair(8, 5);
    a[3] = make_pair(6, 2);
    a[4] = make_pair(2, 3);
    a[5] = make_pair(3, 6);

    sort(a + 1, a + 6);

    for (int i = 1; i <= 5; i++)
        cout << a[i].first << " " << a[i].second << endl;
}

7. Mảng pair và pair lồng nhau

Mảng pair

pair<int, int> p[100];

// p[0], p[1], ..., p[99]
// mỗi phần tử đều có p[i].first và p[i].second

Pair lồng nhau

pair<int, pair<int, int>> p;

p.first = 10;
p.second.first = 20;
p.second.second = 30;
Kinh nghiệm: Pair lồng nhau viết nhanh nhưng khó đọc. Nếu dữ liệu có từ 3 thuộc tính trở lên, nên dùng struct để đặt tên rõ ràng.

8. Bài tập luyện tập PAIR

Các bài dưới đây được biên soạn lại từ tài liệu gốc, phù hợp đưa vào OJ để học sinh luyện tập.

Câu 1. DAOCHU.CPP

string + pair

Bé Bin mới học tên các đồ vật gồm 2 từ như: tu lanh, xe dap, may bay. Hãy đảo ngược thứ tự hai từ của mỗi tên đồ vật.

Dữ liệu

Dòng đầu là số nguyên dương N là số tên đồ vật, N ≤ 50. N dòng tiếp theo, mỗi dòng gồm đúng 2 từ, mỗi từ không quá 5 ký tự, viết thường, không dấu.

Kết quả

In ra N dòng, mỗi dòng là tên sau khi đảo hai từ.

DAOCHU.INP

3
tu lanh
xe dap
nha may

DAOCHU.OUT

lanh tu
dap xe
may nha
Gợi ý: Đọc hai xâu a, b. Có thể lưu bằng pair<string,string>, sau đó in second first.

Câu 2. XEPTIEN.CPP

sort + comparator

Một công ty có N khoản doanh thu, mỗi khoản gồm tiền đầu tưtiền lãi. Hãy sắp xếp doanh thu tăng dần theo tiền lãi. Nếu tiền lãi bằng nhau thì sắp xếp giảm dần theo tiền đầu tư.

Dữ liệu

Dòng đầu là số nguyên dương N. N dòng sau, mỗi dòng gồm hai số nguyên: đầu_tưlãi, mỗi số từ 0 đến 1000.

Kết quả

In ra N dòng là các khoản doanh thu sau khi sắp xếp.

XEPTIEN.INP

4
100 200
130 180
450 300
210 300

XEPTIEN.OUT

130 180
100 200
450 300
210 300
Gợi ý: Lưu mỗi dòng bằng pair<int,int>. Nếu lưu {lai, dau_tu} thì dễ sắp xếp theo lãi. Với điều kiện phụ giảm dần theo đầu tư, cần viết hàm cmp.

Câu 3. CHENHLECH.CPP

pair + đếm tần suất

Trong một bữa tiệc, mỗi cặp nhảy gồm một bạn nam cao x và một bạn nữ cao y. Độ chênh lệch của cặp là |x - y|. Hãy tìm độ chênh lệch xuất hiện nhiều nhất. Nếu có nhiều độ chênh lệch cùng xuất hiện nhiều nhất, chọn độ chênh lệch nhỏ nhất.

Dữ liệu

Dòng đầu là số nguyên dương N ≤ 103. N dòng tiếp theo, mỗi dòng gồm hai số nguyên x, y, với 1 ≤ x, y ≤ 103.

Kết quả

Dòng đầu in ra độ chênh lệch được chọn. Các dòng tiếp theo in các cặp có độ chênh lệch đó theo thứ tự dữ liệu vào.

CHENHLECH.INP

4
5 6
7 8
5 3
9 10

CHENHLECH.OUT

1
5 6
7 8
9 10
Gợi ý: Lưu các cặp bằng vector<pair<int,int>>. Dùng mảng hoặc map để đếm số lần xuất hiện của abs(x-y).

Câu 4. DIEMSO.CPP

xếp hạng

Kỳ thi giữa kỳ gồm 2 môn Tin và Thể dục. Với mỗi học sinh, cần xác định thứ hạng tổng điểm của học sinh đó trong trường. Thứ hạng của một học sinh bằng số học sinh có tổng điểm cao hơn cộng thêm 1.

Dữ liệu

Dòng đầu là số nguyên dương N ≤ 102. N dòng tiếp theo gồm: tên viết tắt của học sinh, điểm Tin x và điểm Thể dục y, với 1 ≤ x, y ≤ 100.

Kết quả

In ra N dòng theo thứ tự dữ liệu vào, mỗi dòng gồm tên học sinh và thứ hạng.

DIEMSO.INP

6
NA 3 5
MT 6 7
TB 5 5
MH 8 8
YN 6 2
PQ 4 3

DIEMSO.OUT

NA 4
MT 2
TB 3
MH 1
YN 4
PQ 6
Gợi ý: Có thể lưu tên và tổng điểm bằng pair<string,int>. Với mỗi học sinh, đếm số học sinh có tổng điểm lớn hơn.

Câu 5. PRJCHAIN.CPP

tham lam + sắp xếp

Công ty nhận N dự án. Dự án i cần chi phí đầu tư ai và sau khi hoàn thành thu về bi. Công ty được thực hiện dự án theo thứ tự tùy ý. Hãy tìm số vốn ban đầu ít nhất cần có để có thể hoàn thành tất cả dự án.

Dữ liệu

Dòng đầu chứa n ≤ 105. Dòng thứ hai chứa n số a1, a2, ..., an. Dòng thứ ba chứa n số b1, b2, ..., bn, các giá trị không quá 109.

Kết quả

In ra một số nguyên là vốn ban đầu tối thiểu.

PRJCHAIN.INP

6
6 3 1 2 4 9
2 4 3 1 3 2

PRJCHAIN.OUT

11
Gợi ý: Lưu mỗi dự án bằng pair<long long,long long> gồm {a[i], b[i]}. Đây là bài tham lam, cần chọn thứ tự làm dự án để giảm vốn ban đầu. Với các dự án lời/lỗ khác nhau, cần phân nhóm và sắp xếp phù hợp.

Tổng kết kiến thức

1. Pair lưu hai giá trị
Sử dụng pair<T1,T2> khi cần gom 2 thông tin.
2. Truy cập bằng first/second
p.first là giá trị thứ nhất, p.second là giá trị thứ hai.
3. Sort pair rất tiện
Mặc định tăng theo first, nếu bằng nhau thì tăng theo second.

9. Cách nhúng bài giảng này vào OJ

Sau khi đưa file PAIR_LIEN_KET.htm lên server, ví dụ tại:

https://hvojbd.ddns.net/docs/LTBT/PAIR_LIEN_KET.htm

Trong phần mô tả bài/kỳ thi của OJ, dán đoạn sau:

<iframe
src="https://hvojbd.ddns.net/docs/LTBT/PAIR_LIEN_KET.htm"
width="100%"
height="850px"
frameborder="0">
</iframe>
File này không cần JavaScript, nên phù hợp để mở ổn định trong iframe của OJ.
style> body { user-select: none; }