Olympic 10 Thành phố 2014 - 2015¶
Bài 1: Số vùng dương¶
Đề bài¶
Trên một lưới \(R \times C\), mỗi ô vuông mang một giá trị nguyên.
Yêu cầu: Xác định số vùng dương (mang số khác 0). Một vùng dương là vùng có các cạnh liền kề bất kể hướng.
Đầu vào: POSITIVE.INP
- Dòng đầu: hai số \(R\) và \(C\) cách nhau ít nhất một khoảng trắng.
- \(R\) dòng kế tiếp, mỗi dòng cho biết \(C\) ký số của dòng.
Đầu ra: POSITIVE.OUT
Một số duy nhất là số vùng dương tìm được.
Ví dụ:
| POSITIVE.INP | POSITIVE.OUT | Giải thích |
|---|---|---|
| 8 7 4 3 2 2 1 0 1 3 3 3 2 1 0 1 2 2 2 2 1 0 0 2 1 1 1 1 0 0 1 1 0 0 0 1 0 0 0 0 1 1 1 0 0 1 2 2 1 1 0 0 1 1 1 2 1 0 |
2 | Hai vùng là vùng in đậm và in nghiên. |
Cách giải đề xuất¶
Ý tưởng chính
Áp dụng thuật toán BFS nhiều lần.
Mỗi lần BFS hoàn thành đồng nghĩa với việc đã duyệt xong một vùng dương trong lưới.
Nói cách khác, khi gặp một ô có giá trị dương và chưa duyệt, ta tính đây là ô bắt đầu của một vùng mới, ta có thể tăng biến đếm số vùng thêm 1.
Viết chương trình
0. Khởi tạo các biến và cấu trúc liên quan:
- Cấu trúc
celldùng để lưu trữ tọa độ hàng và cột. - Mảng
directionsdùng để lưu trữ tám hướng di chuyển khi áp dụng BFS. - Mảng
griddùng để lưu lưới input của đề bài. - Mảng
visisteddùng để đánh dấu những ô đã ghé thăm.
1. Viết hàm bfs() dùng để duyệt lưới grid theo thuật toán BFS.
2. Duyệt từng ô trong lưới, nếu ô có giá trị dương và chưa đánh dấu thì bắt đầu tính là một vùng mới.
Mã nguồn¶
Code đầy đủ được đặt tại:
Bài 2: Vượt sông¶
Đề bài¶
Nhà của bé Cốm nằm ở bên bờ trái của con sông quê, còn nhà ngoại của bé nằm ở bên bờ phải của sông.
Con đường dọc bờ sông có rất nhiều nhánh sông bên trái và bên phải, có nhánh chảy về bên trái, có nhánh chảy về bên phải, có nhánh chảy cả về bên trái và bên phải.
Yêu cầu: Bạn có được sơ đồ đoạn sông từ nhà bé Cốm đến nhà ngoại. Hãy chỉ giúp bé Cốm phương án đi đến nhà ngoại sao cho số lần "vượt sông" là ít nhất.
Đầu vào: RIVER.INP
Gồm một xâu ký tự độ dài \(N (N \le 10^6)\) mô tả bản đồ con sông từ nhà bé đến nhà ngoại. Ký tự 'L' ký hiệu nhánh sông bên trái, ký tự 'R' ký hiệu có nhánh sông bên phải, ký tự 'B' ký hiệu nhánh sông cả bên trái và bên phải.
Đầu ra: RIVER.OUT
Một số duy nhất là số lần ít nhất mà bé phải vượt sông để đến nhà ngoại.
Ví dụ:
| RIVER.INP | RIVER.OUT |
|---|---|
| LLBLRRBRL | 5 |
Ràng buộc: có 50% số text tương ứng 50% số điểm của bài có \(N \le 20\).
Cách giải đề xuất¶
Ý tưởng chính
Đặt vị trí của xuất phát của Cốm là 0. Duyệt từng nhánh sông (biểu thị bằng các ký tự trong chuỗi input), mỗi lần duyệt xong một ký tự thì Cốm đến một vị trí mới.
Vì có hai bờ sông nằm ở bên trái và bên phải nên ứng với một vị trí mới của Cốm, ta xét và lưu số lần vượt sông ít nhất tính từ vị trí 0 đến vị trí mới nhất đối với cả hai bên bờ sông.
Sau khi duyệt hết chuỗi input, ta so sánh giá trị vượt sông giữa hai bờ thì ra được số lần vượt sông ít nhất tính trên tổng thể để đến được nhà ngoại.
Viết chương trình
0. Nhập dữ liệu.
Gọi river là chuỗi input. Ta thêm ký tự '0' vào đầu chuỗi river nhằm "hợp lý hoá" tiến trình suy luận: để đến được vị trí i, Cốm phải xét nhánh sông river[i], với i sẽ được tính bắt đầu từ 1.
1. Khởi tạo vị trí xuất phát của Cốm.
Do nhà Cốm nằm ở bờ trái và đây là vị trí i == 0, nên để đến được vị trí 0 của bờ trái, Cốm không cần vượt sông. Còn để đến được vị trí 0 của bờ phải, Cốm phải băng ngang sông, nghĩa là số lần vượt là 1.
2. Duyệt từng nhánh sông bằng biến i (lưu tại river[i]), với i chạy từ 1 đến n - 1, cũng chính là từng vị trí i mà Cốm sẽ đến, lặp thao tác:
Xét ba trường hợp 'L', 'R' và 'B' của nhánh sông river[i]. Với mỗi trường hợp, ta cần tính số lần vượt sông ít nhất để Cốm đến được vị trí i ở bờ trái và vị trí i ở bờ phải.
Giá trị này được xác định dựa trên số lần vượt sông ít hơn giữa hai lựa chọn: từ vị trí i - 1 ở cùng bờ hoặc từ vị trí i - 1 ở khác bờ.
Vì tính toán tại vị trí i chỉ phụ thuộc vào vị trí liền trước đó, là i - 1, nên việc lưu trữ bằng mảng là không cần thiết. Thay vào đó, ta chỉ dùng bốn biến:
- Biến
current_leftvàcurrent_right: số lần vượt sông ít nhất để đến được bờ trái và bờ phải tại vị tríi - 1. - Biến
next_leftvànext_right: dùng để tính và lưu số lần vượt sông ít nhất tại vị tríi.
3. Sau khi đã vượt qua nhánh sông cuối cùng, Cốm có hai tình huống đến nhà ngoại:
- Tình huống 1: nếu đang đứng ở bờ trái, Cốm cần vượt sông từ bờ trái sang bờ phải, nghĩa là thêm một lần vượt.
- Tình huống 2: nếu đang đứng ở bờ phải, Cốm không cần vượt sông lần nào nữa.
Mã nguồn¶
Code đầy đủ được đặt tại:
Bài 3: Tham quan¶
Nam đang tham quan trên một con đướng với nhiều điểm. Cơn đường xem như một trục số và Nam bắt đầu ở vị trí \(0 (x = 0)\). Có \(N\) điểm được đặt tại vị trí \(x_1, x_2, \cdots, x_n\).
Nam muốn ghé thăm càng nhiều điểm càng tốt trước khi mặt trời lặn và Nam có \(T\) phút. Nam đi bộ 1 đơn vị trong 1 phút.
Nam sẽ đến thăm các điểm theo một thứ tự cụ thể.
Yêu cầu: Hãy giúp xác định số điểm tổi đa mà Nam có thể ghé thăm trước khi thời gian kết thúc.
Đầu vào: XPLORE.INP
- Dòng 1: hai số nguyễn \(T\) và \(N\) cách nhau ít nhất một khoảng trắng, \(1 \le N \le 50000\) và \(1 \le T \le 10^9\).
- \(N\) dòng kế tiếp, mỗi dòng chứa một số nguyên là vị trí của một điểm \(x_i, -100000 \le x_i \le 10^5\).
Đầu ra: XPLORE.OUT
Gồm một số nguyên duy nhất là số điểm có thể ghé thăm.
Ví dụ:
| XPLORE.INP | XPLORE.OUT | Giải thích |
|---|---|---|
| 25 5 10 -3 8 -7 1 |
4 | Nam sẽ ghé thăm bốn điểm là 1, -3, -7 và 8 với tổng thời gian 24 phút. |
Cách giải đề xuất¶
Ý tưởng chính
Đề bài có vẻ như thiếu một yêu cầu: Nam phải luôn ghé thăm điểm gần nhất (mà chưa ghé thăm trước đó).
Nếu không có yêu cầu này thì output có thể khác so với đề bài, đó là Nam có thể ghé thăm tất cả 5 điểm cũng trong 24 phút:
- Từ 0 đi đến -3: 3 phút.
- Từ -3 đi đến -7: 4 phút, tổng là 7 phút.
- Từ -7 đến 1: 8 phút, tổng là 15 phút.
- Từ 1 đến 8: 7 phút, tổng là 22 phút.
- Từ 8 đến 10: 2 phút, tổng là 24 phút.
Nói cách khác, khi không có yêu cầu này, bài toán trở nên phức tạp hơn khi ta phải tìm cách tối ưu hóa để có thể ghé thăm tối đa số điểm. Ngược lại, khi có yêu cầu "phải ghé thăm điểm chưa được ghé thăm gần nhất", bài toán sẽ loại bỏ được sự phức tạp về tối ưu hóa.
Ý tưởng thực hiện tham lam như sau:
- Chia các điểm thành hai nhóm: tọa độ dương
positivesvà tọa độ âmnegatives. - Sắp xếp các tọa độ dương theo thứ tự tăng dần và các tọa độ âm theo thứ tự giảm dần.
-
Dùng hai con trỏ:
pdành cho mảngpositivesvàndành cho mảngnegatives, để so sánh khoảng cách từ vị trí hiện tại của Nam đến:- điểm có tọa độ dương chưa được ghé thăm tiếp theo
- điểm có tọa độ âm chưa được ghé thăm tiếp theo
-
Chọn điểm gần hơn giữa
p_distancevàn_distance, cập nhật thời gian đã trôi quaelapsed_time. Trong trường hợp khoảng cách bằng nhau, ta chọn điểm có tọa độ âm. - Dừng thuật toán khi thời gian trôi qua
elapsed_timevượt quáT.
Viết chương trình
0. Khai báo các biến liên quan.
1. Sắp xếp hai mảng tọa độ dương và tọa độ âm.
2. Dùng hai con trỏ để duyệt hai mảng tọa độ dương và tọa độ âm. Với mỗi lần lặp:
-
Tính khoảng cách từ vị trí hiện tại của Nam đến:
- điểm có tọa độ dương:
abs(positives[p] - current) - điểm có tọa độ âm:
abs(positives[] - current)
- điểm có tọa độ dương:
-
Chọn ra khoảng cách nhỏ hơn giữa
n_distancevàp_distance. - Cập nhật số điểm ghé thăm
visited_count.
Mã nguồn¶
Code đầy đủ được đặt tại:
