Try   HackMD

Đề HSG9 - BRVT - 2023: Editorial

Bài 1: Tìm ước chung lớn nhất (6 điểm)

Cho dãy số nguyên dương

A1,A2,,An
(1n2105)
(1Ai106)
.

Tìm ước chung lớn nhất của hai số sao cho giá trị đó là lớn nhất.

Brute - force:

Duyệt tất cả các cặp số của dãy và tính ước chung lớn nhất, lấy kết quả là giá trị lớn nhất.

Độ phức tạp thời gian:

O(n2).

Code
#include <bits/stdc++.h> using namespace std; const int N = 2e5; int n, res, A[N+1]; int main() { cin >> n; for (int i = 1; i <= n; i++) { cin >> A[i]; for (int j = 1; j <= i-1; j++) { res = max(res, __gcd(A[i], A[j])); } } cout << res; return 0; }

Accepted:

Nhận thấy

Ai106, ta có thể thực hiện thuật toán như sau:

Với mỗi số

i từ
1106
, ta duyệt qua tất cả các bội của
i
, nếu gặp một số tồn tại trong dãy
a
, ta tăng
cnti
lên
1
đơn vị.

Sau khi chạy xong thuật toán trên, ta có

cnti là số lượng số trong dãy
A
nhận
i
là ước.

Đáp án của bài toán là

i lớn nhất sao cho
cnti2
.

Độ phức tạp thời gian:

O(106log106).

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Lưu ý:

Thuật toán trên được gọi là "sàng ước". Nhiều người nhầm tưởng thuật toán này có độ phức tạp thời gian lớn, tuy nhiên, vì số lượng bội số không ngừng giảm khi số tăng lên, nên thực chất thuật toán này chạy rất nhanh.

Nếu coi

n là giới hạn để duyệt
i
, ta có thể tính được độ phức tạp thời gian của thuật toán này như sau:

i=1nninlogn

Code
#include <bits/stdc++.h> using namespace std; const int N = 1e6; int n, cnt[N+1], ex[N+1]; int main() { cin >> n; for (int i = 1; i <= n; i++) { int x; cin >> x; ex[x]++; } for (int i = 1; i <= N; i++) { for (int j = i; j <= N; j += i) { cnt[i] += ex[j]; } } for (int i = N; i >= 1; i--) { if (cnt[i] >= 2) { cout << i; break; } } return 0; }

Bài 2: Đố vui tin học (6 điểm)

Cho dãy số nguyên dương

A1,A2,,An
(n104)
.

Xác định độ dài dãy con tăng dài nhất của dãy

A, sao cho số sau phải lớn hơn số trước ít nhất
k
(k103)
.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Định nghĩa:

Dãy con: Một dãy số được hình thành bằng cách xóa một số phần tử (hoặc không xoá phần tử nào) trong dãy ban đầu và không làm thay đổi thứ tự của các phần tử còn lại.
VD:

3,11,4 là một dãy con của dãy
3,2,11,4,5
.

i=abi : Tổng các số từ
ab
.

Brute - force:

Quay lui thử tất cả các dãy con.

Độ phức tạp thời gian:

O(2n).

Accepted:

Quy hoạch động cơ bản.

Định nghĩa

dpi là dãy con dài nhất thỏa mãn yêu cầu đề bài và bắt buộc kết thúc tại
ai
.

Khởi gán

Công thức quy hoạch động:

  • dpi=1
    (1in)
    : Khởi gán trường hợp dãy có một phần tử.
  • dpi=max(dpi,dpj+1)
    (j<i,aiajk)
    .

Kết quả:

max(dp1,dp2,,dpn).

Độ phức tạp thời gian:

O(n2).

Code
#include <bits/stdc++.h> using namespace std; const int N = 1e4; int n, k, res, A[N+1], dp[N+1]; int main() { cin >> n >> k; for (int i = 1; i <= n; i++) { cin >> A[i]; dp[i] = 1; for (int j = 1; j <= i-1; j++) { if (A[i] - A[j] >= k) { dp[i] = max(dp[i], dp[j] + 1); } } res = max(res, dp[i]); } cout << res; return 0; }

Bài 3: Trò chơi (8 điểm)

Cho

n ô vuông, mỗi ô vuông có giá trị năng lượng
hi
(1n105)
.

Nếu đang ở ô vuông thứ

i, chỉ có thể nhảy đến ô
i+1,i+2,,i+k
(1k103)
.

Để di chuyển từ ô

i đến ô
j
cần chi phí năng lượng là
|hihj|
.

Tìm chi phí nhỏ nhất để đi từ ô

1 đến ô
n
.

Accepted:

Quy hoạch động cơ bản.

Định nghĩa

dpi là chi phí nhỏ nhất để nhảy từ ô
1
đến ô
i
.

Công thức quy hoạch động:

  • dp1=0
    : Khởi gán.
  • dpi=max(dpi,dpj+|hihj|)
    (max(1,ik)j<i)

Kết quả:

dpn.

Độ phức tạp thời gian:

O(nk).

Code
#include <bits/stdc++.h> using namespace std; const int N = 1e5; int n, k, H[N+1], dp[N+1]; int main() { cin >> n >> k; for (int i = 1; i <= n; i++) { cin >> H[i]; } for (int i = 2; i <= n; i++) { dp[i] = dp[i-1] + abs(H[i] - H[i-1]); for (int j = i-2; j >= max(1, i-k); j--) { dp[i] = min(dp[i], dp[j] + abs(H[i] - H[j])); } } cout << dp[n]; return 0; }