2023-2024 Thành phố Hồ Chí Minh¶
Bài 1: XẾP HẠNG¶
Đề bài¶
Trong kỳ thi lập trình có N đội tham dự được đánh số từ 1 đến N. Theo quy định của ban tổ chức, số điểm cuối cùng một đội nhận được là hai chỉ số \((a, b)\) lần lượt là số bài tập đội đó giải được và tổng số điểm phạt của tất cả các bài đã giải.
Điểm phạt cho một bài của mỗi đội được tính qua số lần nộp không thành công bài đó. Khi bắt đầu cuộc thi, tất cả các đội có điểm số là (0, 0).
Xét hai đội \(X_1\) và \(X_2\) lần lượt có điểm số là \((a_1, b_1)\) và \((a_2, b_2)\). Đội \(X_1\) được xem là có điểm số tốt hơn đội \(X_2\) khi hoặc \(a_1 > a_2\) hoặc khi \(a_1 = a_2\) và \(b_1 < b_2\).
Một đội được xếp hạng \(H\) nếu có \(H - 1\) đội có điểm số tốt hơn đội đó.
Khi có một đội nộp thành công một bài, ban tổ chức sẽ thông báo cho tất cả các đội biết đội nào đã nộp thành công và điểm phạt cho bài nộp thành công của đội đó.
Bạn là thành viên của đội số 1 và muốn biết thứ hạng của đội mình mỗi khi có đội nộp thành công.
Yêu cầu: hãy viết một chương trình cho biết thứ hạng của đội 1 mỗi khi có đội nộp bài thành công.
Dữ liệu: XEPHANG.INP
-
Dòng đầu chứa hai số nguyên N, M lần lượt cho biết số đội tham dự và số lần các đội nộp bài thành công.
-
Mỗi dòng trong M dòng tiếp theo chứa hai số nguyên \(T, P (1 \le T \le N; 1 \le P \le 1000)\) cho biết đội \(T\) đã nộp một bài thành công và có điểm phạt cho bài nộp này là \(P\).
Các lần thông báo được cho theo thứ tự thời gian nộp bài tăng dần.
Kết quả: XEPHANG.OUT
Với mỗi sự kiện nộp bài thành công, hãy xuất một số nguyên trên một dòng cho biết thứ hạng của đội 1 vào ngay sau thời điểm đó.
Ràng buộc:
- 40% số điểm của bài: \(1 \le N, M \le 100\) và mỗi đội giải thành công tối đa một bài.
- 80% số điểm của bài: \(1 \le N, M \le 1000\).
- 100% số điểm của bài: \(1 \le N, M \le 100000\).
Ví dụ:
XEPHANG.INP | XEPHANG.OUT |
---|---|
3 5 2 4 3 2 1 3 1 8 2 7 |
2 3 2 1 1 |
Giải thích:
Sự kiện | Điểm đội 1 | Điểm đội 2 | Điểm đội 3 | Hạng đội 1 |
---|---|---|---|---|
(2, 4) | (0, 0) | (1, 4) | (0, 0) | 2 |
(3, 2) | (0, 0) | (1, 4) | (1, 2) | 3 |
(1, 3) | (1, 3) | (1, 4) | (1, 2) | 2 |
(1, 8) | (2, 11) | (1, 4) | (1, 2) | 1 |
(2, 7) | (2, 11) | (2, 11) | (1, 2) | 1 |
Bài giải đề xuất¶
Ý tưởng chính là mỗi khi có một đội nộp bài thành công, ta sẽ cập nhật điểm số của đội đó và gọi một hàm để so sánh với đội 1 để xác định thứ hạng của đội 1.
Bước 0: Khai báo cấu trúc dữ liệu để lưu trữ thông tin của mỗi đội
Bước 1: Viết hàm so sánh hai đội với nhau
Bước 2: Viết hàm xếp hạng
Bước 3: Vừa đọc dữ liệu input vừa thực hiện xếp hạng
Mỗi khi có đội tốt hơn đội 1, ta sẽ tăng biến rank
, là hạng của đội 1, lên 1.
Mã nguồn¶
Code đầy đủ được đặt tại GitHub.
Bài 2: CHỌN CAM¶
Đề bài¶
Một băng chuyền có N khay đựng cam được đánh số liên tiếp từ 1 đến \(N\).
Mỗi khay chứa hai quả cam, mỗi quả cam được dán nhãn phân loại từ 1 đến 5.
Trong đợt khuyến mãi này, người mua chỉ được chọn mua một loại cam bất kỳ trong một dãy liên tiếp các khay và mỗi khay người mua phải chọn một trong hai quả cam chứa trong khay đó.
Yêu cầu: cho biết phân loại các quả cam trong \(N\) khay liên tiếp. Hãy viết một chương trình cho biết số lượng quả cam nhiều nhất một khách hàng có thể mua và loại cam tương ứng.
Dữ liệu: CHONCAM.INP
-
Dòng đầu chứa một số nguyên \(N\).
-
Mỗi dòng trong \(N\) dòng tiếp theo chứa hai số nguyên \(A, B (1 \le A, B \le 5)\) cho biết phân loại của 2 quả cam trong lần lượt các khay.
Kết quả: CHONCAM.OUT
Hai số nguyên lần lượt cho biết số lượng quả cam nhiều nhất một khách hàng có thể mua và loại cam tương ứng.
Trong trường hợp có nhiều kết quả giống nhau hãy xuất ra kết quả ứng với loại cam nhỏ nhất.
Ràng buộc:
- 40% số điểm của bài: \(N \le 100\)
- 80% số điểm của bài: \(N \le 10000\)
- 100% số điểm của bài: \(N \le 100000\)
Ví dụ:
CHONCAM.INP | CHONCAM.OUT |
---|---|
2 1 2 3 1 |
2 1 |
3 1 2 2 3 4 3 |
2 2 |
Bài giải đề xuất¶
Ý tưởng chính là ghi nhận số lượng cam bằng mảng tần số f
.
Để kiểm soát tính liên tiếp của cùng một loại cam, ta thực hiện đánh dấu bằng mảng continuous
.
Bước 0: Khởi tạo
Khởi tạo mảng tần số f
và mảng đánh dấu continuous
.
Bước 1: Viết hàm đánh dấu tính liên tiếp của các loại cam
Bước 2: Cập nhật mảng tần số f
Lần lượt đọc mỗi khay gồm hai trái cam, cập nhật mảng tần số f
và đánh dấu tính liên tiếp của các loại cam.
Nếu loại cam đang xét vẫn liên tiếp thì cộng dồn vào mảng tần số f
. Ngược lại, không còn liên tiếp, thì đặt lại tần số là 1.
Gọi hàm mark()
để đánh dấu tính liên tiếp cho tất cả loại cam.
Bước 3: In kết quả
Dựa vào mảng tần số f
để in ra giá trị và chỉ số của phần tử tương ứng cần tìm.
Mã nguồn¶
Code đầy đủ được đặt tại GitHub.
Bài 3: RÚT VÁN¶
Đề bài¶
Trò chơi rút ván thiết kế trên một vùng hình chữ nhật được bao bởi hai đoạn thẳng có độ dài là \(A\) và hai đoạn thẳng có độ dài là \(B\). Ta có thể xem hai điểm góc chéo nhau của vùng chơi có tọa độ là \((0; 0)\) và \((A; B)\), \(1 \le A, B \le 10^9\).
Có \(N\) thanh dọc có độ dài \(B\), một đầu của thanh dọc thứ \(i\) được đặt tại điểm có tọa độ \((a_i; 0)\), đầu còn lại nằm trên điểm \((a_i; B)\), \(0 < a_i < A\).
Có \(M\) thanh ngang có độ dài \(A\), một đầu của thanh ngang thứ $\(j\) được đặt tại điểm có tọa độ \((0; b_j)\), đầu còn lại nằm trên điểm \((A; bj), 0 < b_j < B\).
Cách bố trí các thanh dọc, ngang như trên chia hình chữ nhật ban đầu thành \((N + 1)(M + 1)\) khu vực khép kín.
Tại những điểm giao nhau, ta cắt thanh dọc và thanh ngang để tạo ra những đoạn nhỏ hơn.
Nhiệm vụ của người chơi là cần bỏ đi các đoạn sao cho tất cả khu vực trong vùng chơi thông với nhau và tổng độ dài các đoạn bỏ đi là ít nhất. Có thể hiểu rằng khi tất cả khu vực thông nhau thì trong vùng chơi từ một khu vực ta có thể di chuyển đến một khu vực bất kỳ.
Hình minh họa ví dụ bên dưới: cần bỏ các đoạn nét đứt để các khu vực thông nhau.
Yêu cầu: hãy viết một chương trình cho biết tổng độ dài ít nhất các đoạn cần bỏ đi để tất cả khu vực trong vùng chơi thông nhau.
Dữ liệu: RUTVAN.INP
- Dòng đầu chứa 4 số nguyên \(A, B, N, M\).
- Mỗi dòng trong \(N\) dòng tiếp theo chứa một số nguyên lần lượt là vị trí của các thanh dọc \(a_1, a_2, ..., a_N\).
- Mỗi dòng trong \(M\) dòng tiếp theo chứa một số nguyên lần lượt là vị trí của các thanh ngang \(b_1, b_2, ..., b_M\).
Kết quả: RUTVAN.OUT
Một số nguyên cho biết tổng độ dài ít nhất các đoạn cần bỏ đi để tất cả các khu vực trong vùng chơi thông nhau.
Ràng buộc:
- 30% số điểm của bài: \(0 \le N, M \le 3\)
- 60% số điểm của bài: \(0 \le N, M \le 1000\)
- 100% số điểm của bài: \(0 \le N, M \le 25000\)
Ví dụ:
RUTVAN.INP | RUTVAN.OUT |
---|---|
5 5 2 2 1 3 3 4 |
10 |
Bài giải đề xuất¶
Ý tưởng chính:
Nếu như xem mỗi vùng là một đỉnh của đồ thị, mỗi đoạn của thanh (ngang hoặc dọc) là một cạnh của đồ thị, thì cách giải quyết bài toán này tương tự cách xác định cây khung tối thiểu bằng thuật toán Kruskal.
Bước 0:
Khởi tạo mảng Xs
và Ys
để lưu trữ vị trí của các thanh dọc và thanh ngang, kể cả các đoạn thẳng A và B bao bên ngoài.
Bước 1: Tính độ dài của các đoạn cắt
Lần lượt dựa trên hoành độ và tung độ của các điểm giao nhau, ta sẽ tính độ dài của các đoạn cắt và đưa vào mảng lengths
.
Bước 2: Thực hiện "rút ván"
Thực hiện tương tự thuật toán Kruskal:
- Sắp xếp các đoạn cắt theo thứ tự độ dài tăng dần
- Tính số vùng ban đầu
- Tính tổng độ dài của các đoạn được cắt. Trong đó, số phần tử tham gia tính tổng là số vùng trừ đi 1.
Mã nguồn¶
Code đầy đủ được đặt tại GitHub.