Xếp que diêm (Tin học trẻ toàn quốc 2024 – Vòng chung kết toàn quốc – Bảng B)
Đề bài
Người ta dùng que diêm để xếp các chữ số từ $0$ đến $9$ giống như trên đồng hồ điện tử (mỗi nét là một que diêm). Số que cần cho từng chữ số như hình dưới (số ghi bên dưới mỗi chữ số):
Cho số tự nhiên $N$ có ba chữ số. Xếp $N$ bằng que diêm theo đúng cách trên. Hỏi nếu chỉ dùng đúng số que diêm đó (không thừa, không thiếu) thì xếp được số có ba chữ số nhỏ nhất và lớn nhất là bao nhiêu? Số xếp được không được có chữ số $0$ ở đầu.
Dữ liệu vào: một dòng chứa số $N$ ($100 \le N \le 999$).
Kết quả: dòng thứ nhất ghi số có ba chữ số nhỏ nhất xếp được; dòng thứ hai ghi số có ba chữ số lớn nhất xếp được.
Giới hạn thời gian: 1 giây. Dữ liệu nhập từ bàn phím, in ra màn hình.
123104
971Giải thích: số $123$ cần $2 + 5 + 5 = 12$ que diêm. Các số $104$ ($2 + 6 + 4$) và $971$ ($6 + 4 + 2$) cũng cần đúng $12$ que, và không có số ba chữ số nào nhỏ hơn $104$ hay lớn hơn $971$ dùng đúng 12 que.
Lời mở đầu
Đây là bài "khởi động" của Bảng B vòng chung kết. Chỉ có $900$ số có ba chữ số nên duyệt hết là đủ full điểm. Nhưng bài vẫn có hai cái bẫy nho nhỏ mà mình muốn các bạn để ý: chữ số 7 trong hình của đề dùng 4 que (không phải 3 như nhiều người quen), và khi làm cách tham lam thì không có chữ số nào dùng đúng 3 que. Mình sẽ cho các bạn thấy cả kết quả chạy thật của các bản "dính bẫy".
Link này chỉ học viên đã tham gia tổ chức mới vào được. Học viên nên ưu tiên nộp ở đây vì sẽ được chấm nhanh hơn.
Nộp bài trên CLUE OJLộ trình lời giải, bấm vào từng phần để nhảy tới:
Bộ testBộ test có gì?
Bộ test trên CLUE do mình tự sinh: 20 test (test 1 là ví dụ của đề), gồm các số đặc biệt như $100$, $999$, $111$, $888$, các số có chữ số $7$, cộng thêm các số ngẫu nhiên. Đề không chia subtask (mọi test đều có $N$ ba chữ số).
| Số test | Tổng số que $S$ | Số lượng số có 3 chữ số xếp được bằng $S$ que | Chỉ có đúng 1 số (nhỏ nhất = lớn nhất) | Có chữ số 7 trong $N$ hoặc trong đáp án |
|---|---|---|---|---|
| 20 | từ 6 đến 21 | từ 1 đến 153 | test 2–3 | 6 test |
- Hai test chỉ có đúng một số xếp được: $S = 6$ que thì chỉ có số $111$, $S = 21$ que thì chỉ có $888$ (vì mỗi chữ số dùng nhiều nhất 7 que).
- Có 6 test liên quan tới chữ số 7 – đây là các test bắt lỗi đếm sai số que của chữ số 7.
Kết quả chạy thật các cách bên dưới:
| Subtask | Điều kiện | Test | Cách 1 C++ | Cách 1 Python | Cách 2 C++ | Cách 2 Python |
|---|---|---|---|---|---|---|
| Toàn bộ (100%) | $100 \le N \le 999$ | 1–20 | 20/20 | 20/20 | 20/20 | 20/20 |
Ý tưởngĐếm que rồi tìm số cùng số que
Đầu tiên chép đúng bảng số que từ hình của đề vào một mảng:
| Chữ số | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Số que | 6 | 2 | 5 | 5 | 4 | 5 | 6 | 4 | 7 | 6 |
Tính $S$ = tổng số que của ba chữ số của $N$. Bài toán trở thành: tìm số có ba chữ số nhỏ nhất và lớn nhất có tổng số que đúng bằng $S$. Có hai cách nghĩ:
- Duyệt: thử hết $900$ số từ $100$ tới $999$ (cách 1).
- Tham lam: muốn số nhỏ nhất thì chữ số hàng trăm phải nhỏ nhất có thể, rồi tới hàng chục, hàng đơn vị – miễn là phần còn lại vẫn xếp được (cách 2). Cách này dùng được cả khi $N$ có rất nhiều chữ số.
Em nhập $N$ rồi bấm Tiếp để xem cách tham lam dựng từng chữ số (chữ số cam là chữ số đang thử, xanh là đã chốt):
Cách 1Duyệt mọi số có ba chữ số (full)
Ba vòng lặp lồng nhau cho chữ số hàng trăm $a$ (từ 1, vì không có số 0 ở đầu), hàng chục $b$ và hàng đơn vị $c$. Vì duyệt theo thứ tự tăng dần nên số đầu tiên có tổng que bằng $S$ là số nhỏ nhất, số cuối cùng là số lớn nhất.
n / 100,n / 10 % 10,n % 10lần lượt là chữ số hàng trăm, hàng chục, hàng đơn vị của $N$.nho == -1nghĩa là chưa tìm được số nào – số đầu tiên tìm được chính là số nhỏ nhất. Luôn có ít nhất một số thoả mãn (chính $N$!) nên không bao giờ in ra $-1$.
Kết quả chạy thật: Bản C++ đúng 20/20 test, chạy lâu nhất 0.004s. Bản Python đúng 20/20 test, chạy lâu nhất 0.027s. Full điểm.
Trên đồng hồ điện tử quen thuộc, chữ số 7 thường chỉ có 3 nét. Nhưng trong hình của đề, chữ số 7 có thêm nét trái trên nên dùng 4 que. Mình thử sửa mảng thành {6, 2, 5, 5, 4, 5, 6, 3, 7, 6} (chữ số 7 dùng 3 que) rồi chạy trên bộ test: bản này chỉ đúng 12/20 test, sai test 1, 7–8, 10, 14, 17–18, 20 (các test đúng chạy lâu nhất 0.004s). Ví dụ của đề ($123$) vẫn ra $104$ nhưng số lớn nhất lại ra $977$ – sai ngay! Bài học: đếm que từ hình của đề, đừng đếm theo trí nhớ.
Cách 2Tham lam từng chữ số (mở rộng cho số nhiều chữ số)
Nếu đề cho $N$ có tới 15 chữ số thì không thể duyệt hết $10^{15}$ số. Ta dựng đáp án từng chữ số từ trái sang phải:
- Muốn số nhỏ nhất: ở mỗi vị trí, thử chữ số $d$ từ $0$ (hoặc từ $1$ nếu là vị trí đầu) lên $9$; chốt chữ số đầu tiên mà số que còn lại $r = s - que[d]$ vẫn đủ để xếp các vị trí phía sau.
- Muốn số lớn nhất: làm giống hệt nhưng thử $d$ từ $9$ xuống $0$.
Vì sao đúng? Hai số cùng số chữ số thì số nào có chữ số đầu tiên (khác nhau) nhỏ hơn sẽ nhỏ hơn – nên chữ số hàng cao phải được ưu tiên tuyệt đối, miễn là phía sau vẫn còn cách xếp.
Mỗi chữ số dùng từ 2 tới 7 que, nên nhiều bạn kiểm tra 2 * con <= r && r <= 7 * con (còn con chữ số phía sau). Sai! Không có chữ số nào dùng đúng 3 que, nên 1 chữ số không thể xếp bằng 3 que. Bản kiểm tra kiểu này: chỉ đúng 18/20 test, sai test 17–18 (các test đúng chạy lâu nhất 0.004s) – ví dụ $N = 418$ ($S = 13$) sẽ chốt "97" rồi kẹt lại vì chữ số cuối cần đúng 3 que.
Cách chắc chắn: tính trước bảng duoc[i][x] = "có xếp được $i$ chữ số (chữ số nào cũng được, kể cả 0) bằng đúng $x$ que không" bằng quy hoạch động nhỏ: duoc[0][0] = true, và duoc[i][x] = true nếu có chữ số $d$ để duoc[i-1][x - que[d]] đúng.
- Đọc $N$ dưới dạng
stringđể code dùng được cho số có bao nhiêu chữ số cũng được (k = n.size()). - Biểu thức
nho ? t : 9 - tgiúp dùng chung một hàm cho cả hai chiều:tchạy 0→9 thìdchạy 0→9 (số nhỏ nhất) hoặc 9→0 (số lớn nhất). - Ở mỗi vị trí luôn tìm được chữ số để chốt, vì chính các chữ số của $N$ là một cách xếp hợp lệ.
Kết quả chạy thật: Bản C++ đúng 20/20 test, chạy lâu nhất 0.003s. Bản Python đúng 20/20 test, chạy lâu nhất 0.028s. Mình còn cho hai cách chạy trên cả 900 giá trị $N$ có thể và so sánh – kết quả giống hệt nhau.
Tổng kếtKết quả chạy thật trên bộ test
| Cách | Ngôn ngữ | Đúng | Lâu nhất (test đúng) | Điểm theo subtask |
|---|---|---|---|---|
| Bẫy: chữ số 7 dùng 3 que | C++ | 12/20 | 0.004s | 0% |
| 1. Duyệt 900 số | C++ | 20/20 | 0.004s | 100% |
| Python | 20/20 | 0.027s | 100% | |
| Bẫy: coi mọi số que 2..7 đều xếp được | C++ | 18/20 | 0.004s | 0% |
| 2. Tham lam từng chữ số | C++ | 20/20 | 0.003s | 100% |
| Python | 20/20 | 0.028s | 100% |