Khoá học C++ nâng cao
Bài học: Quy Hoạch Động Chữ Số (Digit DP)
🏆 +100 XP tiềm năng Thoát

Quy Hoạch Động Chữ Số (Digit DP)

2 khối nội dung 45 phút học tập MỞ BẢNG TRẮNG

Quy Hoạch Động Chữ Số (Digit DP)

Nội dung bài học

1. Khái niệm & bản chất của Quy hoạch động chữ số

Quy hoạch động chữ số (Digit DP) là phương pháp chuyên dùng để giải quyết các bài toán: Đếm số lượng số nguyên trong đoạn $[L, R]$ thỏa mãn một tính chất chữ số đặc biệt (ví dụ: tổng chữ số bằng $K$, không chứa chữ số 4 và 7, các chữ số tăng dần, số nguyên tố, số chia hết cho $D$).

Với $L, R \le 10^{18}$, duyệt trâu từng số mất $10^{18}$ phép tính $\implies$ TLE.
Digit DP giải quyết bài toán bằng cách:

  1. Chuyển đổi bài toán đoạn: $\text{Count}([L, R]) = f(R) - f(L - 1)$ với $f(X)$ là số lượng số thỏa mãn trong $[0, X]$.
  2. Biểu diễn số $X$ thành mảng các chữ số $D_0, D_1, \dots, D_{M-1}$ ($M \le 19$).
  3. Xây dựng số từ trái sang phải qua hàm đệ quy có nhớ memo[index][tight][leading_zero][state].

Mô hình phân nhánh Digit DP

2. Các tham số trạng thái

  1. index (Vị trí chữ số hiện tại): Duyệt từ chữ số đầu tiên (cao nhất) $0$ đến chữ số cuối cùng $M - 1$.
  2. tight (Cờ giới hạn cận trên): - tight = true: Các chữ số phía trước đều đã chọn trùng khít với các chữ số của $X$. Chữ số hiện tại chỉ được chọn từ $0$ đến $D_{index}$. - tight = false: Đã có ít nhất một chữ số phía trước chọn nhỏ hơn $D$, số hiện tại được tự do chọn từ $0$ đến $9$.
  3. leading_zero (Cờ số 0 vô nghĩa ở đầu): Xác định xem ta đã bắt đầu viết số thực tế chưa hay vẫn đang là các số 0 vô nghĩa (ảnh hưởng đến việc đếm chữ số 0).
  4. state (Trạng thái đặc thù của bài toán): Ví dụ tổng các chữ số đã chọn, số dư khi chia cho $K$, mặt nạ bit của các chữ số đã xuất hiện.

3. Mẫu cài đặt chuẩn thi đấu: Đếm số có tổng chữ số bằng $S$ trong đoạn $[L, R]$

#include <bits/stdc++.h>
using namespace std;

string num_str;
long long dp[20][2][200]; // dp[index][tight][sum]
int target_sum;

long long digit_dp(int idx, bool tight, int current_sum) {
    if (idx == num_str.size()) {
        return (current_sum == target_sum ? 1 : 0);
    }
    if (dp[idx][tight][current_sum] != -1) {
        return dp[idx][tight][current_sum];
    }

    int limit = (tight ? (num_str[idx] - '0') : 9);
    long long total = 0;

    for (int digit = 0; digit <= limit; ++digit) {
        bool next_tight = tight && (digit == limit);
        total += digit_dp(idx + 1, next_tight, current_sum + digit);
    }

    return dp[idx][tight][current_sum] = total;
}

long long count_valid(long long x) {
    if (x < 0) return 0;
    num_str = to_string(x);
    memset(dp, -1, sizeof(dp));
    return digit_dp(0, true, 0);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    long long L, R;
    if (!(cin >> L >> R >> target_sum)) return 0;

    cout << count_valid(R) - count_valid(L - 1) << "\n";
    return 0;
}

4. Ranh giới áp dụng

Dạng Bài Cận Biên $R$ Kỹ Thuật Tối Ưu Độ Phức Tạp
Đếm số theo tính chất chữ số $R \le 10^{18}$ Digit DP $\mathcal{O}(\text{Length}(R) \times \text{States} \times 10) \approx 19 \times 200 \times 10 < 10^5$
Đếm số theo tính chất đại số lớn $R \le 10^9$ Sàng / Toán học / Bù trừ PIE $\mathcal{O}(\sqrt{R})$ hoặc $\mathcal{O}(1)$

1
A
B
C
D
2
A
B
C
D

Bài tập lập trình vận dụng

22 Bài tập thực hành

Nhấn vào nút "Làm bài" bên dưới để mở giao diện làm bài trực tuyến (Online Judge) và thực hiện viết mã nguồn cho bài tập này.

Đếm số có tổng chữ số bằng k
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tổng các chữ số bằng k trong đoạn [l, r]
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Đếm số lượng chữ số 0 xuất hiện
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Số có các chữ số tăng ngặt
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Số chia hết cho tổng các chữ số của chính nó
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Đếm số đối xứng (palindrome numbers) trong đoạn
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Số chứa đầy đủ các chữ số từ 0 đến 9
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tổng các số trong đoạn thỏa mãn tính chất chữ số
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Số có tích các chữ số bằng k
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tổng giá trị các số thỏa mãn tính chất chữ số
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Đếm số tự mãn (số armstrong / narcissistic) trong đoạn
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Đếm số đẹp có hiệu hai chữ số kề nhau $\ge 2$ (số stepping)
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Số có tổng bình phương các chữ số là số nguyên tố
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tìm số thỏa mãn điều kiện chữ số thứ k nhỏ nhất
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Số chia hết cho tất cả các chữ số khác không của nó
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Tổng xor chữ số của mọi số trong đoạn $[l, r]$
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Digit DP chia het cho k
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Digit DP khong chua chu so cam
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Digit DP so doi xung palindrome
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Digit DP tong binh phuong chu so
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Digit DP dem so nguyen to chu so
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài
Digit DP tich cac chu so
Độ khó: mediumĐiểm tối đa: 100.0 điểm
Làm bài