Olympic 10 Thành phố 2016 - 2017¶
Bài 1: Đặt cân¶
Đề bài¶
Cho \(n\) quả cân có khối lượng lần lượt là \(P[1], P[2], \cdots, P[n]\) và một cái cân có hai đĩa.
Yêu cầu: Tìm số cách đặt lên hai đĩa một số quả cân nào đó từ \(n\) quả cân trên sao cho cân thăng bằng.
Đầu vào: DATCAN.INP
- Dòng đầu là số \(n\).
- Dòng tiếp theo là các khối lượng của các quả cân.
Đầu ra: DATCAN.OUT
Một số nguyên duy nhất là số cách đặt cân. Nếu không có cách nào thì ghi số 0.
Ví dụ:
| DATCAN.INP | DATCAN.OUT |
|---|---|
| 5 1 2 3 4 5 |
7 |
Cách giải đề xuất¶
Ý tưởng chính
1. Thay vì theo dõi tổng khối lượng của từng đĩa, ta có thể theo dõi mức chênh lệch khối lượng giữa chúng.
Gọi $d[i][j]là số cách chọn một số quả cân bất kỳ từiquả cân đầu tiên sao cho chênh lệch khối lượng của hai đĩa làj`.
Ta có:
d[i][j] = d[i - 1][j] + d[i - 1][j - p[i]] + d[i - 1][j + p[i]]
Trong đó:
ilà quả cân đang xét.jlà mức độ chệnh lệch giữa hai đĩa, nằm trong phạm vi[-total, +total], vớitotallà tổng khối lượng củanquả cân.+ p[i]là đặt quả cânilên đĩa trái.- p[i]là đặt quả cânilên đĩa phải.
2. Sau khi duyệt hết n quả cân, d[n][total] sẽ là số cách đặt cân sao cho hai đĩa thăng bằng.
Ta phải trừ đi 1 để bỏ đi cách mà hai đĩa không có quả cân nào.
Tiếp theo, ta phải chia 2, vì hai đĩa cân là đối xứng, đặt a quả cân lên đĩa trái và b quả cân lên đĩa phải cũng giống như đặt a lên đĩa phải và b lên đĩa trái.
Như vậy, result = (d[n][total] - 1) / 2
3. Quan sát công thức trên, ta thấy để tính toán ở bước i, ta chỉ cần truy xuất bước i - 1. Các giá trị trước đó từ bước 0 đến i - 2 là không cần thiết.
Do đó, thay vì dùng bảng có n hàng (để i chạy từ 0 đến n - 1), ta chỉ cần hai hàng, đồng nghĩa hai mảng:
- Mảng
prev_d[j]tương đương hàngi - 1, tức bước trước đó. - Mảng
d[j]tương đương hàngi, tức bước hiện tại đang xét.
Viết chương trình
1. Khởi tạo mảng prev_d dùng để lưu số cách ở bước trước đó.
2. Điền giá trị vô bảng quy hoạch.
3. Tính số cách thỏa yêu cầu bài toán.
Mã nguồn¶
Code đầy đủ được đặt tại:
Bài 2: Đổ xí ngầu¶
Đề bài¶
Bờm yêu thích chơi những trò chơi với các con số và đã mua 3 viên xí ngầu về.
Các viên xí ngầu này có lần lượt \(S1, S2, S3\) mặt. Các mặt được đánh số từ \(1 \rightarrow S1, 1 \rightarrow S2, 1 \rightarrow S3\).
Bờm liên tục đổ xí ngầu và mỗi lần ghi nhận một số nguyên là tổng giá trị của 3 mặt xí ngầu đổ được. Mục đích của Bờm là tìm ra tổng giá trị nào xuất hiện nhiều nhất.
Yêu cầu: Cho số mặt của 3 xí ngầu, hãy xác định xem tổng giá trị nào xuất hiện nhiều nhất. Nếu có nhiều hơn 1 giá trị xuất hiện nhiều nhất thì ghi ra giá trị nhỏ nhất. Giả sử xác suất xuất hiện của các mặt xí ngầu là như nhau.
Đầu vào: DOXINGAU.INP
3 số nguyên cách nhau bởi dấu cách: \(S1, S2, S3 (2 \le S1 \le 20; 2 \le S2 \le 20; 2 \le S3 \le 40)\).
Đầu ra: DOXINGAU.OUT
Số nguyên nhỏ nhất là tổng giá trị xuất hiện nhiều lần nhất.
Ví dụ:
| DOXINGAU.INP | DOXINGAU.OUT |
|---|---|
| 3 2 3 | 5 |
Giải thích:
Đây là tất cả các trường hợp có thể xảy ra.
1 1 1 -> 3
1 1 2 -> 4
1 1 3 -> 5
1 2 1 -> 4
1 2 2 -> 5
1 2 3 -> 6
2 1 1 -> 4
2 1 2 -> 5
2 1 3 -> 6
2 2 1 -> 5
2 2 2 -> 6
2 2 3 -> 7
3 1 1 -> 5
3 1 2 -> 6
3 1 3 -> 7
3 2 1 -> 6
3 2 2 -> 7
3 2 3 -> 8
Trong đó, 5 và 6 xuất hiền nhiều lần nhất, mỗi số là 5 lần. Vậy kết quả là 5.
Cách giải đề xuất¶
Ý tưởng chính
1. Các ràng buộc về số mặt trên mỗi viên xí ngầu khá nhỏ, chỉ tối đa 20, 20 và 40. Do đó, ta chỉ cần vét cạn để tìm ra tổng xuất hiện thường xuyên nhất.
2. Để lưu số lần xuất hiện của mỗi tổng, ta sử dụng mảng tần số counter.
Số phần tử tối đa của counter là 20 + 20 + 40 + 1 = 81.
Viết chương trình
1. Dùng ba vòng lặp để vét cạn các mặt của ba viên xí ngầu: cập nhật tần số của tổng trong mảng counter.
2. Tìm chỉ số của phần tử lớn nhất trong counter, đây chính là tổng xuất hiện nhiều nhất.
Mã nguồn¶
Code đầy đủ được đặt tại: