Chỉnh hợp
Đề bài
Hãy in ra tất cả các chỉnh hợp chập $k$ của các số nguyên dương $1, 2, 3, \ldots, n$.
Chỉnh hợp là cách chọn những phần tử từ một nhóm lớn hơn và có phân biệt thứ tự.
Input: một dòng gồm hai số nguyên $n, k$.
Output: mỗi dòng gồm $k$ số nguyên là một chỉnh hợp. Các chỉnh hợp cần được in ra theo thứ tự từ điển.
Điều kiện: $1 \le k < n \le 7$.
3 21 2
1 3
2 1
2 3
3 1
3 2Lời mở đầu
Chỉnh hợp chập $k$ của $n$ là một dãy $k$ số khác nhau lấy từ $1..n$, đổi thứ tự là thành chỉnh hợp khác (1 2 khác 2 1). Bài này dạy một kỹ thuật cực kỳ quan trọng của quay lui: mảng đánh dấu used[] – đánh dấu khi chọn, bỏ đánh dấu khi quay lui. Bài sau (Hoán vị) chính là trường hợp $k = n$ của bài này.
Bấm vào nút bên dưới để nộp bài và được hệ thống CLUE OJ chấm tự động.
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 chính thức có gì?
Bộ test có 21 test, phủ đủ mọi cặp $(n, k)$ với $2 \le n \le 7$, $1 \le k < n$:
| n | Các test | Các giá trị k | Số dòng in ra $A_n^k$ |
|---|---|---|---|
| 2 | 1 | 1 | 2 |
| 3 | 2–3 | 1, 2 | 3, 6 |
| 4 | 4–6 | 1, 2, 3 | 4, 12, 24 |
| 5 | 7–10 | 1, 2, 3, 4 | 5, 20, 60, 120 |
| 6 | 11–15 | 1, 2, 3, 4, 5 | 6, 30, 120, 360, 720 |
| 7 | 16–21 | 1, 2, 3, 4, 5, 6 | 7, 42, 210, 840, 2.520, 5.040 |
Nhiều nhất là test $n = 7, k = 6$ với $7!/1! = 5040$ dòng – rất nhỏ. Bài không chia subtask; chỉ cần quay lui đúng là qua hết.
Ý tưởngChọn từng vị trí, số nào dùng rồi thì đánh dấu
Ta điền lần lượt $x_1, x_2, \ldots, x_k$. Hàm Try(i) chọn số cho vị trí $i$: thử $v = 1, 2, \ldots, n$, nhưng bỏ qua những số đã xuất hiện ở các vị trí trước. Để biết nhanh "số $v$ đã dùng chưa" (thay vì dò lại cả dãy), ta giữ mảng used[v]:
x[i] = v; used[v] = true; → Đi tiếp: đủ $k$ số thì in, chưa đủ thì Try(i + 1) → Quay lui: used[v] = false; để số $v$ được dùng lại ở các nhánh khác.Thử $v$ từ nhỏ tới lớn ở mọi vị trí nên các chỉnh hợp ra đúng thứ tự từ điển. Em nhập $n, k$ (tối đa 4) rồi bấm Tiếp – nút đỏ ✗ là số bị bỏ qua vì đã dùng:
CodeCài đặt
- Dòng
used[v] = false;là linh hồn của quay lui: thiếu nó thì sau chỉnh hợp đầu tiên, số 1 bị đánh dấu "đã dùng" mãi mãi và ta mất hết các chỉnh hợp có số 1 ở vị trí sau. - Mảng
usedkhai báo toàn cục nên ban đầu toànfalse; mỗi lần đi xuống rồi quay lên, nó trở về đúng trạng thái cũ. - Mỗi dòng in thừa một dấu cách ở cuối – không sao, trình chấm so sánh theo từng số.
Số nút của cây là $A_n^1 + A_n^2 + \cdots + A_n^k$, mỗi nút thử $n$ giá trị. Với $n = 7$ chỉ vài chục nghìn phép toán.
Kết quả chạy thật: C++: đúng 21/21 test, test chậm nhất chạy 0.004 giây. Bản Python: Python: đúng 21/21 test, test chậm nhất chạy 0.026 giây.
used[] đạt 21/21 test trên bộ test của CLUE OJ (cả C++ lẫn Python). Các bạn nhớ khuôn "đánh dấu → gọi đệ quy → bỏ đánh dấu" này, nó sẽ quay lại trong rất nhiều bài: hoán vị, n quân hậu, tô màu đồ thị...- Không biết học gì trước, học gì sau, học lan man nên kiến thức bị hổng.
- Bí bài không ai gỡ, ngồi cả buổi vẫn không ra rồi bỏ cuộc.
- Code chạy được nhưng không đủ điểm, không biết sai ở đâu, chậm ở đâu.
- Không có đề thi thử và người chấm, nên không biết mình đang ở mức nào.
- Dễ nản giữa chừng khi không có ai theo sát.
- Lộ trình 111 buổi video sắp sẵn từ dòng code đầu tiên đến đề HSG thật của các tỉnh.
- Thầy trực tiếp gỡ bí, chữa bài cho từng em qua nhóm Zalo riêng.
- Hệ thống chấm bài tự động 24/7 và thi thử HSG hằng tuần để biết chính xác mình đang ở đâu.
- Đóng học phí một lần, thầy kèm lâu dài: từ thi chuyên Tin đến HSG quốc gia và cả khi lên đại học.
Nhận xét
Đăng nhận xét