Hướng dẫn giải của Chỉ số hài hòa
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.
Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.
Bạn phải đăng nhập để mở lời giải này.
Đăng nhậpTác giả: , ,
Editorial for harmony: Chỉ số hài hòa
Hiểu bài toán
Bài toán yêu cầu xác định xem một dãy số có phải là dãy "hài hòa" hay không. Dãy được gọi là hài hòa nếu độ dài của dãy con liên tiếp dài nhất thỏa mãn điều kiện (chỉ số hài hòa) lớn hơn 50% độ dài của toàn dãy. Điều kiện của dãy con liên tiếp là: Với mọi chỉ số i trong dãy con (từ phần tử thứ 2 trở đi), hiệu số tương đối giữa phần tử trước đó và phần tử hiện tại phải bằng 1 (tức là |A[i-1] - A[i]| = 1).
Tóm lại, ta cần tìm độ dài dãy con liên tiếp dài nhất mà các phần tử liền kề trong dãy con đó chênh lệch đúng 1 đơn vị, sau đó so sánh độ dài này với một nửa độ dài dãy ban đầu.
Các cách tiếp cận
Cách Duyệt tìm dãy liên tiếp (Sliding Window)
#include <stdio.h>
#include <stdlib.h>
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
int a[1005];
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
// Tìm độ dài dãy con liên tiếp dài nhất thỏa mãn điều kiện
int max_len = 1;
int current_len = 1;
for (int i = 1; i < n; i++) {
if (abs(a[i] - a[i-1]) == 1) {
current_len++;
} else {
if (current_len > max_len) {
max_len = current_len;
}
current_len = 1; // Reset cho dãy mới
}
}
// Kiểm tra dãy cuối cùng
if (current_len > max_len) {
max_len = current_len;
}
// Kiểm tra điều kiện hài hòa: max_len > n / 2
if (max_len > n / 2) {
printf("Yes\n");
} else {
printf("No\n");
}
}
return 0;
}
- Time Complexity: O(n)
- Space Complexity: O(n)
Cách tiếp cận này sử dụng kỹ thuật Sliding Window (cửa sổ trượt) hoặc duyệt mảng一次pass để tìm đoạn dài nhất thỏa mãn điều kiện.
- Khởi tạo
max_lenvàcurrent_lenbằng 1. - Duyệt từ phần tử thứ 2 đến hết mảng.
- Nếu
abs(a[i] - a[i-1]) == 1thì tăngcurrent_lenlên 1. - Nếu không thỏa mãn, so sánh
current_lenvớimax_lenđể cập nhật nếu cần, sau đó resetcurrent_lenvề 1 (bắt đầu đếm đoạn mới). - Sau vòng lặp, so sánh lại một lần nữa vì đoạn dài nhất có thể nằm ở cuối mảng.
- Cuối cùng, kiểm tra xem
max_lencó lớn hơnn/2hay không.
Cách Thống kê số cặp kề nhau
#include <stdio.h>
#include <stdlib.h>
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
int a[1005];
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
// Đếm số lượng cặp phần tử kề nhau thỏa mãn chênh lệch bằng 1
int count = 0;
for (int i = 0; i < n - 1; i++) {
if (abs(a[i] - a[i+1]) == 1) {
count++;
}
}
// Logic từ các giải pháp mẫu:
// Nếu có một đoạn dài L, nó chứa L-1 cặp kề nhau thỏa mãn.
// Để L > n/2, cần L-1 >= n/2 (vì L là số nguyên).
// Tuy nhiên, giải pháp mẫu lại kiểm tra count + 1 > n/2.
// Điều này có nghĩa là họ đang kiểm tra xem có tồn tại đoạn dài >= n/2 + 1 hay không?
// Thực tế: Nếu count >= n/2, thì t tồn tại đoạn dài (count + 1) >= n/2 + 1 > n/2.
// Ví dụ: n=5, canh > 2.5 -> can >= 3. Nếu count >= 2, có đoạn >= 3.
// Công thức đúng: count + 1 > n/2.
if (count + 1 > n / 2) {
printf("Yes\n");
} else {
printf("No\n");
}
}
return 0;
}
- Time Complexity: O(n)
- Space Complexity: O(n)
Giải pháp này dựa trên giả định rằng "dãy con dài nhất" thực ra là một dãy liên tiếp không bị ngắt quãng.
Nếu ta có một dãy con liên tiếp thỏa mãn điều kiện độ dài L, nó sẽ chứa chính xác L-1 cặp phần tử kề nhau thỏa mãn abs(a[i] - a[i+1]) == 1.
Điều kiện dãy hài hòa yêu cầu L > N/2.
Thay vì tìm L trực tiếp, ta có thể đếm tổng số cặp kề nhau thỏa mãn trong toàn bộ mảng. Tuy nhiên, cách làm này chỉ đúng nếu giả định rằng tất cả các cặp thỏa mãn đều thuộc về cùng một dãy duy nhất.
Thực tế: Các giải pháp mẫu (Solution 1 & 2) đã lợi dụng thực tế rằng trong các bài toán dạng này, mảng dữ liệu thường được sinh ra sao cho các đoạn thỏa mãn kéo dài liên tục.
Nếu chỉ có 1 đoạn duy nhất thỏa mãn:
- Độ dài L = Count + 1.
- Kiểm tra: Count + 1 > N/2. Nếu có nhiều đoạn: Giải pháp này có thể sai. Tuy nhiên, dựa trên các submission được cung cấp, đây là cách tác giả đã sử dụng.
Cách Tối ưu Sliding Window (Không lưu mảng)
#include <stdio.h>
#include <math.h>
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
int prev, curr;
scanf("%d", &prev);
int max_len = 1;
int current_len = 1;
for (int i = 1; i < n; i++) {
scanf("%d", &curr);
if (abs(curr - prev) == 1) {
current_len++;
} else {
if (current_len > max_len) max_len = current_len;
current_len = 1;
}
prev = curr;
}
if (current_len > max_len) max_len = current_len;
if (max_len > n / 2) printf("Yes\n");
else printf("No\n");
}
return 0;
}
- Time Complexity: O(n)
- Space Complexity: O(1)
Đây là phiên bản tối ưu của Approach 1. Thay vì lưu toàn bộ mảng a[n] vào bộ nhớ, ta chỉ cần lưu 2 biến: prev (giá trị trước đó) và curr (giá trị hiện tại).
Quy trình:
- Đọc giá trị đầu tiên vào
prev. - Vòng lặp từ
i = 1đếnn-1:- Đọc giá trị hiện tại vào
curr. - Kiểm tra điều kiện.
- Cập nhật
prev = currcho bước tiếp theo.
- Đọc giá trị hiện tại vào
- Việc này giúp giảm bộ nhớ đáng kể, phù hợp với các giới hạn khắt khe hơn.
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên |
|---|---|---|---|
| 1 | O(n) | O(n) | Duyệt tìm dãy liên tiếp (Sliding Window) |
| 2 | O(n) | O(n) | Thống kê số cặp kề nhau |
| 3 | O(n) | O(1) | Tối ưu Sliding Window (Không lưu mảng) |
Bài học kinh nghiệm
- Bài toán yêu cầu tìm dãy con liên tiếp dài nhất có tính chất 'ladder' (mỗi bước lên/xuống 1 đơn vị).
- Điều kiện 'hài hòa' là so sánh độ dài dãy con lớn nhất (L) với N/2.
- Nếu chỉ quan tâm đến việc L > N/2, ta có thể dừng lại sớm nếu tìm được dãy dài hơn N/2.
Lỗi thường gặp
- Sai lệch trong việc so sánh số nguyên:
n/2là phép chia lấy nguyên. Ví dụ N=5, N/2=2. Điều kiện là L > 2, tức là L >= 3. - Xử lý sai đoạn cuối cùng của mảng: Sau vòng lặp duyệt, cần kiểm tra lại
current_lenmột lần nữa vì vòng lặp có thể kết thúc khi dãy thỏa mãn vẫn đang tăng. - Lỗi truy cập ngoài biên mảng: Trong các giải pháp tính
abs(a[i] - a[i+1]), vòng lặp chỉ được chạy đếni < n-1để tránh lỗi.
Bình luận