Giai đoạn 3 – Thuật toán
Mục tiêu giai đoạn: HS nắm các kỹ thuật thường gặp ở bài 3–4 của đề: vét cạn, đệ quy/quay lui, tham lam, hai con trỏ, tìm kiếm nhị phân, quy hoạch động cơ bản, loang trên lưới.
Quan trọng hơn cả: biết lấy điểm từng phần – luôn có lời giải "trâu" trước khi tối ưu.
Bảng tiến độ
| Buổi | Chủ đề | Nội dung chính | Bài tập gợi ý |
|---|---|---|---|
| 29 | Duyệt / vét cạn | Thử mọi khả năng; giảm số vòng lặp bằng toán | Tìm x, y, z nguyên dương với x + y + z = n và điều kiện; đếm bộ ba Pytago ≤ n |
| 30 | Đệ quy | Hàm gọi chính nó, điều kiện dừng; sys.setrecursionlimit | Giai thừa, Fibonacci, tổng chữ số đệ quy, tháp Hà Nội |
| 31 | Quay lui | Sinh dãy nhị phân, hoán vị, tổ hợp; itertools.product / permutations / combinations | Liệt kê xâu nhị phân độ dài n, hoán vị 1..n, chọn k trong n |
| 32 | Luyện tập vét cạn | Duyệt tập con (n ≤ 20) | Tập con có tổng bằng S; chia kẹo thành 2 phần chênh lệch ít nhất |
| 33 | Tham lam | Chọn phương án tốt nhất ở mỗi bước; tìm phản ví dụ | Đổi tiền ít tờ nhất; chọn nhiều hoạt động không giao nhau nhất (sort theo giờ kết thúc); xếp hàng chờ ít nhất |
| 34 | Luyện tập tham lam | Sort + tham lam | Ghép cặp chênh lệch nhỏ nhất, chở hàng với xe tải trọng W, số lớn nhất ghép từ các số |
| 35 | Hai con trỏ | Hai chỉ số cùng chạy trên dãy | Dãy đã sort – cặp tổng bằng k; đoạn dài nhất có tổng ≤ S (số dương); trộn hai dãy tăng |
| 36 | Tìm kiếm nhị phân | Tự viết; bisect_left, bisect_right | Kiểm tra x có trong dãy (q truy vấn); đếm số phần tử trong [l, r] |
| 37 | Chặt nhị phân đáp án | Đáp án "đơn điệu" → tìm nhị phân trên đáp án | Cắt gỗ (chiều cao cưa lớn nhất); in sách vào k ngày (mỗi ngày đọc ít nhất bao nhiêu trang) |
| 38 | Quy hoạch động nhập môn | Ghi nhớ kết quả bài toán con; công thức truy hồi | Leo cầu thang 1–2 bậc; số đường đi trên lưới; Fibonacci n ≤ 10⁵ (mod 10⁹+7) |
| 39 | QHĐ cơ bản | Bảng phương án 1 chiều / 2 chiều | Dãy con tăng dài nhất O(n²); đổi tiền ít xu nhất; cái túi 0/1 (n ≤ 100, W ≤ 10⁴) |
| 40 | Luyện tập QHĐ | Nhận dạng bài QHĐ | Tổng lớn nhất không chọn 2 số kề nhau; ★★★ xâu con chung dài nhất |
| 41 | Loang (BFS) trên lưới | Hàng đợi deque, 4 hướng đi | Đếm số hòn đảo; đường đi ngắn nhất trong mê cung |
| 42 | Luyện tập loang | Loang nhiều nguồn, DFS bằng ngăn xếp | Diện tích vùng lớn nhất; lửa cháy lan; ★★★ đồ thị đơn giản (danh sách kề) |
| 43 | Ôn tập tổng hợp | Nhận dạng bài: đọc đề → đoán kỹ thuật | 6–8 bài trộn lẫn các dạng, chỉ cần nói hướng giải, code 2–3 bài |
| 44 | Thi thử lần 3 | 3–4 bài, 150 phút, đúng thể lệ | Lấy đề Tin học trẻ năm trước |
Code mẫu cốt lõi
Quay lui – sinh mọi xâu nhị phân độ dài n
n = int(input()) x = [0] * n def thu(i): if i == n: print("".join(map(str, x))) return for v in (0, 1): x[i] = v thu(i + 1) thu(0)
Khi thi được dùng thư viện cho nhanh:
from itertools import product, permutations, combinations for t in product([0, 1], repeat=n): ... for p in permutations(range(1, n + 1)): ... for c in combinations(a, k): ...
Tham lam – chọn nhiều hoạt động nhất
hd = sorted(hd, key=lambda x: x[1]) # sắp theo giờ kết thúc dem, ket_thuc = 0, -1 for bd, kt in hd: if bd >= ket_thuc: dem += 1 ket_thuc = kt
Hai con trỏ – đoạn dài nhất có tổng ≤ S (các số dương)
best = tong = l = 0 for r in range(n): tong += a[r] while tong > S: tong -= a[l] l += 1 best = max(best, r - l + 1)
Tìm kiếm nhị phân
from bisect import bisect_left, bisect_right a.sort() dem = bisect_right(a, r) - bisect_left(a, l) # số phần tử trong [l, r] # Chặt nhị phân đáp án: tìm x nhỏ nhất mà dat(x) đúng lo, hi = 0, 10**18 while lo < hi: mid = (lo + hi) // 2 if dat(mid): hi = mid else: lo = mid + 1
Gợi nhớ trò chơi đoán số (Buổi 6): mỗi lần đoán loại được một nửa → chỉ cần ~log₂n lần.
QHĐ – leo cầu thang
# f[i] = số cách lên bậc i, mỗi bước 1 hoặc 2 bậc f = [0] * (n + 1) f[0] = 1 for i in range(1, n + 1): f[i] = f[i - 1] + (f[i - 2] if i >= 2 else 0)
Cách dạy QHĐ: luôn hỏi 3 câu – (1) f[i] nghĩa là gì? (2) f[i] tính từ đâu? (3) giá trị đầu tiên là gì?
Loang BFS – đếm số hòn đảo
from collections import deque m, n = map(int, input().split()) g = [input().strip() for _ in range(m)] # '#': đất, '.': nước da_tham = [[False] * n for _ in range(m)] dem = 0 for i in range(m): for j in range(n): if g[i][j] == '#' and not da_tham[i][j]: dem += 1 q = deque([(i, j)]) da_tham[i][j] = True while q: x, y = q.popleft() for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): u, v = x + dx, y + dy if 0 <= u < m and 0 <= v < n and g[u][v] == '#' and not da_tham[u][v]: da_tham[u][v] = True q.append((u, v)) print(dem)
Liên hệ Scratch: giống công cụ "đổ màu" (tô loang) trong trình vẽ.
Phân hóa cho 2 học sinh
- Lớp 7: ưu tiên vét cạn, tham lam, tìm kiếm nhị phân trên mảng, QHĐ 1 chiều (buổi 29, 31, 33, 35, 36, 38). Đây là những phần mang lại nhiều điểm nhất.
- Lớp 8: đi hết bảng, thêm chặt nhị phân đáp án, QHĐ 2 chiều, loang BFS.
- Không cần dạy: cây phân đoạn, đồ thị nâng cao (Dijkstra…), hình học – hiếm gặp ở bảng THCS và tốn nhiều thời gian. Nếu em lớp 8 tiến bộ nhanh và đi tiếp vòng toàn quốc, bổ sung sau.