Hướng dẫn giải của Thiết kế tam giác


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
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ập

Tác giả: Hiếu Nguyễn, dra2k8, PDC1, MandI

Editorial for tricount: Thiết kế tam giác

Hiểu bài toán

Cho N thanh sắt với độ dài lần lượt là A_i. Yêu cầu đếm số bộ ba chỉ số (i, j, k) với 1 ≤ i < j < k ≤ N sao cho ba thanh sắt tương ứng có độ dài tạo thành một tam giác. Điều kiện để ba cạnh a, b, c tạo thành tam giác là a + b > c (với giả định a ≤ b ≤ c).

Các cách tiếp cận

Cách Brute Force
long long cnt = 0;
for (int i = 0; i < N; i++) {
    for (int j = i + 1; j < N; j++) {
        for (int k = j + 1; k < N; k++) {
            if (A[i] + A[j] > A[k]) cnt++;
        }
    }
}
  • Time Complexity: O(N^3)
  • Space Complexity: O(N)

Duyệt tất cả các bộ ba (i, j, k) có i < j < k và kiểm tra điều kiện A[i] + A[j] > A[k]. Với N tối đa 8000, độ phức tạp này quá lớn và không thể chấp nhận.

Cách Binary Search
sort(A.begin(), A.end());
ll cnt = 0;
for (int i = 0; i < N; i++) {
    for (int j = i + 1; j < N; j++) {
        int sum = A[i] + A[j];
        int lo = j + 1, hi = N - 1, pos = j;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (A[mid] < sum) {
                pos = mid;
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }
        cnt += (pos - j);
    }
}
  • Time Complexity: O(N^2 log N)
  • Space Complexity: O(N)

Sau khi sắp xếp mảng, với mỗi cặp (i, j) cố định, ta cần tìm số lượng k > j sao cho A[k] < A[i] + A[j]. Do mảng đã sắp xếp, ta có thể dùng binary search để tìm vị trí cuối cùng (pos) thỏa mãn A[pos] < A[i] + A[j]. Số lượng k hợp lệ là pos - j. Với N=8000, độ phức tạp khoảng 8000^2 * log(8000) ≈ 7.4*10^8, có thể chạy trong 1s nhưng khá sát.

Cách Two Pointers
sort(a, a + n);
long long ans = 0;
for (int k = 0; k < n; ++k) {
    int i = 0, j = k - 1;
    while (i < j) {
        if (a[i] + a[j] > a[k]) {
            ans += (j - i);
            --j;
        } else {
            ++i;
        }
    }
}
  • Time Complexity: O(N^2)
  • Space Complexity: O(N)

Sau khi sắp xếp, ta xét biến k làm cạnh lớn nhất. Với mỗi k, ta dùng hai con trỏ i (bắt đầu từ 0) và j (bắt đầu từ k-1). Nếu a[i] + a[j] > a[k], thì mọi chỉ số từ i đến j-1 đều thỏa mãn cùng với j (vì a[i] tăng dần), nên ta cộng j-i vào kết quả và giảm j. Ngược lại, ta tăng i để tìm giá trị a[i] lớn hơn. Cách này tối ưu hơn binary search vì chỉ cần duyệt O(N) cho mỗi k.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(N^3) O(N) Brute Force
2 O(N^2 log N) O(N) Binary Search
3 O(N^2) O(N) Two Pointers

Bài học kinh nghiệm

  • Sau khi sắp xếp mảng, điều kiện tam giác chỉ cần kiểm tra a + b > c với a ≤ b ≤ c.
  • Với biến k cố định (cạnh lớn nhất), bài toán trở thành tìm số cặp (i, j) với i < j < k sao cho a[i] + a[j] > a[k].
  • Kỹ thuật hai con trỏ (two pointers) có thể giải quyết bài toán con này trong thời gian tuyến tính O(k).

Lỗi thường gặp

  • Quên sắp xếp mảng trước khi áp dụng các thuật toán tối ưu.
  • Sử dụng biến số(int) cho kết quả trung gian thay vì long long do số lượng bộ ba có thể lên tới ~N^3/6 (với N=8000, kết quả ~8.5*10^10).
  • Lỗi chỉ số trong vòng lặp two pointers: j phải bắt đầu từ k-1 và điều kiện dừng là i < j.

Bình luận

Please read the guidelines before commenting.


Không có bình luận tại thời điểm này.