Bài 11.4: Các dạng bài tập mảng 1 chiều cơ bản trong C++

Bài 11.4: Các dạng bài tập mảng 1 chiều cơ bản trong C++
Chào mừng các bạn quay trở lại với series học lập trình C++!
Sau khi đã tìm hiểu về khái niệm mảng 1 chiều (one-dimensional array) trong bài trước, hôm nay chúng ta sẽ cùng nhau đi sâu vào thực hành qua các dạng bài tập cơ bản và phổ biến nhất. Việc nắm vững các thao tác cơ bản với mảng là cực kỳ quan trọng, vì mảng là cấu trúc dữ liệu nền tảng, xuất hiện rất nhiều trong các bài toán lập trình thực tế.
Chúng ta sẽ lần lượt khám phá các dạng bài tập sau:
- Nhập và xuất mảng
- Tính tổng và trung bình cộng các phần tử
- Tìm kiếm phần tử nhỏ nhất, lớn nhất (và vị trí của chúng)
- Tìm kiếm một giá trị cụ thể trong mảng
- Đếm số lượng phần tử thỏa mãn một điều kiện
Bắt tay vào thôi nào!
1. Nhập và xuất mảng
Đây là thao tác cơ bản nhất khi làm việc với mảng. Chúng ta cần biết cách đưa dữ liệu vào mảng từ bàn phím (nhập) và hiển thị dữ liệu của mảng ra màn hình (xuất).
Để làm điều này, chúng ta thường sử dụng vòng lặp for
để duyệt qua từng phần tử của mảng thông qua chỉ số (index) của chúng.
Mảng 1 chiều trong C++ có các phần tử được đánh chỉ số từ 0
đến kích thước - 1
.
Ví dụ:
#include <iostream>
#include <vector> // Sử dụng vector cho tiện lợi, có thể dùng mảng C-style [] cũng được
int main() {
int n; // Kích thước mảng
cout << "Nhap so luong phan tu cua mang: ";
cin >> n;
// Khai bao mang (su dung vector)
// vector<kieu_du_lieu> ten_mang(kich_thuoc);
vector<int> arr(n);
// --- NHAP MANG ---
cout << "Nhap cac phan tu cua mang:\n";
for (int i = 0; i < n; ++i) {
cout << "arr[" << i << "]: "; // Hien thi chi so de nguoi dung biet nhap gi
cin >> arr[i]; // Nhap gia tri cho phan tu tai chi so i
}
// --- XUAT MANG ---
cout << "\nMang vua nhap la:\n";
for (int i = 0; i < n; ++i) {
cout << arr[i] << (i == n - 1 ? "" : " "); // In phan tu, cach nhau boi dau cach
}
cout << endl; // Xuong dong sau khi in xong mang
return 0;
}
Giải thích code:
- Chúng ta khai báo một biến
n
để lưu kích thước mảng, sau đó yêu cầu người dùng nhập giá trị này. - Sử dụng
vector<int> arr(n);
để tạo một mảngarr
cón
phần tử kiểuint
.vector
là một container linh hoạt hơn mảng C-style, nhưng cách truy cập phần tử và duyệt mảng vẫn tương tự. - Vòng lặp
for (int i = 0; i < n; ++i)
duyệt qua mảng từ chỉ số 0 đếnn-1
. - Trong phần nhập,
cin >> arr[i];
đọc giá trị từ bàn phím và lưu vào phần tử tại chỉ sối
. - Trong phần xuất,
cout << arr[i];
in giá trị của phần tử tại chỉ sối
. Chúng ta thêm dấu cách" "
sau mỗi phần tử (trừ phần tử cuối cùng) để output dễ đọc hơn.
2. Tính tổng và trung bình cộng các phần tử
Đây là một bài tập kinh điển sử dụng vòng lặp để xử lý dữ liệu trong mảng. Để tính tổng, chúng ta cần một biến tích lũy (accumulator), khởi tạo bằng 0, và cộng dồn giá trị của từng phần tử vào biến này khi duyệt mảng. Trung bình cộng đơn giản là tổng chia cho số lượng phần tử.
Ví dụ:
#include <iostream>
#include <vector>
#include <numeric> // Thu vien nay cung co ham accumulate rat tien, nhung ta code tay de hieu ro hon
int main() {
vector<int> arr = {10, 20, 30, 40, 50}; // Mang vi du
int n = arr.size(); // Lay kich thuoc mang
if (n == 0) { // Kiem tra truong hop mang rong
cout << "Mang rong, khong the tinh tong hoac TBC.\n";
return 0;
}
long long sum = 0; // Bien luu tong, nen dung long long phong truong hop tong lon
for (int i = 0; i < n; ++i) {
sum += arr[i]; // Cong gia tri phan tu hien tai vao tong
}
// Tinh trung binh cong
// Ep kieu tong sang double de phep chia khong bi mat phan thap phan
double average = static_cast<double>(sum) / n;
cout << "Mang da cho: ";
for (int i = 0; i < n; ++i) {
cout << arr[i] << (i == n - 1 ? "" : " ");
}
cout << endl;
cout << "Tong cac phan tu: " << sum << endl;
cout << "Trung binh cong: " << average << endl;
return 0;
}
Giải thích code:
- Biến
sum
được khởi tạo bằng0
. Chúng ta dùng kiểulong long
để đảm bảo có thể lưu trữ tổng của các số lớn mà không bị tràn số (overflow). - Vòng lặp
for
duyệt qua từng phần tử.sum += arr[i];
là cách viết tắt củasum = sum + arr[i];
. - Sau khi có tổng
sum
, chúng ta tínhaverage
. Việc sử dụngstatic_cast<double>(sum)
là quan trọng. Nếu không ép kiểu, phép chiasum / n
sẽ là phép chia số nguyên (integer division) và kết quả có thể bị sai nếu tổng không chia hết chon
. Ép kiểusum
thànhdouble
buộc phép chia trở thành phép chia số thực (floating-point division). - Chúng ta cũng thêm một bước kiểm tra nếu mảng rỗng (
n == 0
) để tránh lỗi chia cho 0.
3. Tìm phần tử nhỏ nhất, lớn nhất
Bài toán này yêu cầu tìm giá trị nhỏ nhất (min) và lớn nhất (max) trong mảng. Cách tiếp cận phổ biến là giả sử phần tử đầu tiên là min và max ban đầu, sau đó duyệt qua các phần tử còn lại và cập nhật min/max nếu tìm thấy giá trị nhỏ hơn hoặc lớn hơn.
Ví dụ:
#include <iostream>
#include <vector>
#include <limits> // Co the su dung numeric_limits de khoi tao min/max
int main() {
vector<int> arr = {5, 12, 8, 25, 3, 18}; // Mang vi du
int n = arr.size();
if (n == 0) {
cout << "Mang rong, khong tim duoc min/max.\n";
return 0;
}
// Khoi tao min_val va max_val bang phan tu dau tien
int min_val = arr[0];
int max_val = arr[0];
// Co the khoi tao bang gia tri cuc tieu/cuc dai cua kieu du lieu neu can xu ly mang rong
// int min_val = numeric_limits<int>::max();
// int max_val = numeric_limits<int>::min();
// Khoi tao chi so cua min/max
int min_idx = 0;
int max_idx = 0;
// Duyet tu phan tu thu 2 (chi so 1) tro di
for (int i = 1; i < n; ++i) {
if (arr[i] < min_val) {
min_val = arr[i]; // Cap nhat gia tri min
min_idx = i; // Cap nhat chi so min
}
if (arr[i] > max_val) {
max_val = arr[i]; // Cap nhat gia tri max
max_idx = i; // Cap nhat chi so max
}
}
cout << "Mang da cho: ";
for (int i = 0; i < n; ++i) {
cout << arr[i] << (i == n - 1 ? "" : " ");
}
cout << endl;
cout << "Phan tu nho nhat: " << min_val << " (tai chi so " << min_idx << ")" << endl;
cout << "Phan tu lon nhat: " << max_val << " (tai chi so " << max_idx << ")" << endl;
return 0;
}
Giải thích code:
- Chúng ta khởi tạo
min_val
vàmax_val
bằng giá trị của phần tử đầu tiên (arr[0]
). Đồng thời lưu lại chỉ số của chúng (min_idx = 0
,max_idx = 0
). - Vòng lặp bắt đầu từ chỉ số
1
(for (int i = 1; i < n; ++i)
), vì phần tử ở chỉ số 0 đã được dùng để khởi tạo. - Bên trong vòng lặp, chúng ta so sánh
arr[i]
vớimin_val
vàmax_val
. Nếuarr[i]
nhỏ hơnmin_val
, chúng ta cập nhật cảmin_val
vàmin_idx
. Tương tự vớimax_val
vàmax_idx
.
4. Tìm kiếm một giá trị cụ thể
Bài toán này là kiểm tra xem một giá trị target
có tồn tại trong mảng hay không. Nếu có, chúng ta có thể muốn biết nó xuất hiện ở những vị trí (chỉ số) nào. Cách đơn giản nhất là duyệt qua từng phần tử và so sánh với giá trị target
.
Ví dụ:
#include <iostream>
#include <vector>
int main() {
vector<int> arr = {10, 5, 20, 5, 30, 5}; // Mang vi du co gia tri lap lai
int n = arr.size();
int target = 5; // Gia tri can tim
cout << "Mang da cho: ";
for (int i = 0; i < n; ++i) {
cout << arr[i] << (i == n - 1 ? "" : " ");
}
cout << endl;
cout << "Tim kiem gia tri: " << target << endl;
bool found = false; // Bien co de kiem tra xem co tim thay hay khong
for (int i = 0; i < n; ++i) {
if (arr[i] == target) { // Neu phan tu hien tai bang gia tri target
cout << "Tim thay gia tri " << target << " tai chi so: " << i << endl;
found = true; // Danh dau da tim thay
// Neu chi can tim lan dau tien, co the them 'break;' o day de thoat vong lap
}
}
if (!found) { // Neu bien found van la false sau khi ket thuc vong lap
cout << "Khong tim thay gia tri " << target << " trong mang.\n";
}
return 0;
}
Giải thích code:
- Chúng ta khai báo biến
target
là giá trị cần tìm và biếnfound
kiểubool
, khởi tạo làfalse
. - Vòng lặp duyệt qua từng phần tử.
- Câu lệnh
if (arr[i] == target)
kiểm tra xem phần tử hiện tại có bằngtarget
không. Nếu có, in ra chỉ số và đặtfound = true
. - Nếu muốn tìm tất cả các vị trí của
target
, chúng ta để vòng lặp chạy hết. Nếu chỉ muốn tìm vị trí đầu tiên, thêmbreak;
ngay sau khi tìm thấy. - Sau vòng lặp, kiểm tra biến
found
. Nếu nó vẫn làfalse
, nghĩa là không tìm thấy giá trị nào bằngtarget
trong mảng.
5. Đếm số lượng phần tử thỏa mãn điều kiện
Bài toán này yêu cầu đếm có bao nhiêu phần tử trong mảng thỏa mãn một điều kiện nhất định (ví dụ: số chẵn, số lẻ, số dương, số âm, số lớn hơn 100, v.v.). Cách làm tương tự như tính tổng, nhưng thay vì cộng dồn giá trị, chúng ta tăng một biến đếm mỗi khi điều kiện được thỏa mãn.
Ví dụ: Đếm số lượng phần tử chẵn trong mảng.
#include <iostream>
#include <vector>
int main() {
vector<int> arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // Mang vi du
int n = arr.size();
cout << "Mang da cho: ";
for (int i = 0; i < n; ++i) {
cout << arr[i] << (i == n - 1 ? "" : " ");
}
cout << endl;
int even_count = 0; // Bien dem so chan
for (int i = 0; i < n; ++i) {
// Kiem tra dieu kien: phan tu la so chan (chia het cho 2, phan du bang 0)
if (arr[i] % 2 == 0) {
even_count++; // Tang bien dem
}
}
cout << "So luong phan tu chan trong mang: " << even_count << endl;
return 0;
}
Giải thích code:
- Biến
even_count
được khởi tạo bằng0
. - Vòng lặp duyệt qua từng phần tử.
- Câu lệnh
if (arr[i] % 2 == 0)
kiểm tra điều kiện "phần tửarr[i]
là số chẵn". Toán tử%
(modulo) trả về phần dư của phép chia. Nếu phần dư khi chia cho 2 bằng 0, số đó là số chẵn. - Nếu điều kiện đúng, biến
even_count
được tăng lên 1 (even_count++
). - Sau khi duyệt hết mảng,
even_count
chứa tổng số lượng phần tử chẵn.
Bài tập ví dụ: C++ Bài 7.A4: Tính tổng mảng
Cho một dãy số \(a\) có \(N\) phần từ \(a_1, a_2, a_3,...a_n\).
Hãy làm theo các yêu cầu sau đây:
- Tính tổng các phần tử trong dãy số \(a\).
- Tính giá trị trung bình các phần tử trong dãy số \(a\), lấy phần nguyên.
- Tìm giá trị lớn nhất trong dãy số \(a\).
- Tìm giá trị nhỏ nhất trong dãy số \(a\).
INPUT FORMAT
Dòng đầu tiên chứa số nguyên \(N\) ( \(N \leq 10^6\)).
Dòng thứ hai chứa \(N\) số nguyên, mỗi số các nhau một dấu cách \((a_i < 10^9)\)
OUTPUT FORMAT
Dòng đầu tiên in ra tổng các phần tử trong dãy \(a\).
Dòng thứ hai in ra giá trị trung bình các phần tử trong dãy số \(a\), lấy phần nguyên.
Dòng thứ ba in ra giá trị lớn nhất trong dãy số \(a\).
Dòng thứ tư in ra giá trị nhỏ nhất trong dãy số \(a\).
Ví dụ 1:
Input
3
1 2 3
Ouput
6
2
3
1
Ví dụ 2:
Input
5
5 3 1 6 3
Ouput
18
3
6
1
Giải thích ví dụ mẫu:
Ví dụ 1:
- Tổng các phần tử:
6
- Giá trị trung bình (làm tròn xuống):
2
- Giá trị lớn nhất:
3
- Giá trị nhỏ nhất:
1
- Tổng các phần tử:
Ví dụ 2:
- Tổng các phần tử:
18
- Giá trị trung bình (làm tròn xuống):
3
- Giá trị lớn nhất:
6
- Giá trị nhỏ nhất:
1
<br>
- Tổng các phần tử:
Phân tích và hướng tiếp cận:
- Đọc dữ liệu: Bạn cần đọc số nguyên
N
đầu tiên, sau đó đọcN
số nguyên tiếp theo. - Tính toán:
- Tổng: Bạn cần một biến để lưu tổng. Khi đọc từng số, bạn cộng nó vào biến tổng. Chú ý đến kiểu dữ liệu của biến tổng vì
N
có thể lên đến10^6
và mỗi số có thể gần10^9
, tổng có thể rất lớn. Kiểu dữ liệulong long
trong C++ là phù hợp để lưu trữ tổng này. - Trung bình (nguyên): Sau khi có tổng, bạn chia tổng cho
N
. Vì yêu cầu lấy phần nguyên, phép chia số nguyên (/
) trong C++ sẽ tự động thực hiện điều này (với số dương). Kết quả trung bình cũng nên dùnglong long
để đảm bảo tính toán chính xác trước khi in ra. - Giá trị lớn nhất: Bạn cần một biến để lưu giá trị lớn nhất tìm được cho đến thời điểm hiện tại. Khởi tạo biến này với giá trị của phần tử đầu tiên hoặc một giá trị rất nhỏ. Sau đó, khi đọc từng số tiếp theo, so sánh nó với giá trị lớn nhất đang có và cập nhật nếu số mới lớn hơn.
- Giá trị nhỏ nhất: Tương tự như giá trị lớn nhất, bạn cần một biến lưu giá trị nhỏ nhất. Khởi tạo nó với giá trị của phần tử đầu tiên hoặc một giá trị rất lớn. So sánh từng số mới đọc với giá trị nhỏ nhất đang có và cập nhật nếu số mới nhỏ hơn.
- Tổng: Bạn cần một biến để lưu tổng. Khi đọc từng số, bạn cộng nó vào biến tổng. Chú ý đến kiểu dữ liệu của biến tổng vì
- Hiệu quả: Vì
N
có thể lớn (10^6
), bạn nên xử lý các số khi đọc vào thay vì lưu toàn bộ dãy số vào một mảng (vector) nếu bộ nhớ là vấn đề (mặc dù với10^6
sốint
thì vẫn thường chấp nhận được). Tuy nhiên, cách xử lý từng số khi đọc vào là hiệu quả nhất về bộ nhớ và đủ nhanh cho yêu cầu này (một lần duyệt qua dữ liệu). - Sử dụng std: Bạn có thể sử dụng các hàm có sẵn trong thư viện chuẩn C++ như
max
vàmin
để tìm giá trị lớn nhất/nhỏ nhất một cách ngắn gọn.
Các bước thực hiện (Hướng dẫn):
- Bao gồm các thư viện cần thiết:
iostream
để nhập/xuất vàalgorithm
(chomax
,min
). - Đọc giá trị
N
. - Khai báo các biến cần thiết:
- Một biến kiểu
long long
để tính tổng, khởi tạo bằng 0. - Hai biến kiểu
int
để lưu giá trị lớn nhất và nhỏ nhất. - Một biến kiểu
int
để đọc từng số trong dãy.
- Một biến kiểu
- Xử lý số đầu tiên: Đọc số đầu tiên của dãy. Dùng số này để khởi tạo giá trị ban đầu cho biến lớn nhất và nhỏ nhất. Cộng số này vào tổng.
- Vòng lặp: Dùng vòng lặp để đọc và xử lý
N-1
số còn lại của dãy.- Trong mỗi lần lặp, đọc số tiếp theo.
- Cộng số vừa đọc vào biến tổng.
- Sử dụng
max
để cập nhật biến lớn nhất: so sánh biến lớn nhất hiện tại với số vừa đọc và gán kết quả lớn hơn vào biến lớn nhất. - Sử dụng
min
để cập nhật biến nhỏ nhất: so sánh biến nhỏ nhất hiện tại với số vừa đọc và gán kết quả nhỏ hơn vào biến nhỏ nhất.
- Tính trung bình: Sau vòng lặp, tính giá trị trung bình nguyên bằng cách chia tổng cho
N
(sử dụng phép chia/
). Lưu ý:N
làint
, tổng làlong long
. Phép chia giữalong long
vàint
sẽ tự động trả vềlong long
. - In kết quả: In lần lượt tổng, trung bình (nguyên), giá trị lớn nhất và giá trị nhỏ nhất trên các dòng riêng biệt theo đúng định dạng yêu cầu.
Gợi ý về code (Không phải code hoàn chỉnh):
#include <iostream> // Để nhập/xuất
#include <algorithm> // Để dùng max, min
int main() {
int N;
// Đọc N
long long sum = 0; // Khởi tạo tổng
int max_val; // Biến lưu max
int min_val; // Biến lưu min
int current_num; // Biến đọc từng số
// Đọc số đầu tiên VÀ dùng nó để khởi tạo max_val và min_val
// Đồng thời cộng nó vào sum
// Lặp N-1 lần tiếp theo:
// Đọc current_num
// Cộng current_num vào sum
// Cập nhật max_val = max(max_val, current_num);
// Cập nhật min_val = min(min_val, current_num);
// Sau vòng lặp, tính trung bình (nguyên) = sum / N
// In ra sum
// In ra trung bình
// In ra max_val
// In ra min_val
return 0; // Kết thúc chương trình
}
Comments