給定範圍 [L, R],統計滿足特定數字屬性(如包含某數字、數位和為特定值、無重複數字等)的整數個數。這類「數位 DP」問題是 ACM 競賽高頻考點,NJUPT OJ 1050 即為典型代表。
dfs(pos, tight, lead, state...)
long long dfs(int pos, bool tight, bool lead, ...) {
if (pos == -1) return check(state); // 到達最低位,判斷是否合法
if (!tight && !lead && memo[pos][state] != -1) return memo[pos][state];
int up = tight ? digits[pos] : 9;
long long ans = 0;
for (int d = 0; d <= up; ++d) {
ans += dfs(pos-1, tight && d==up, lead && d==0, new_state);
}
if (!tight && !lead) memo[pos][state] = ans;
return ans;
}
| 問題類型 | 核心狀態 | 示例 |
|---|---|---|
| 包含/不包含某數字 | bool has_digit | 統計不含 4 的數字個數 |
| 數位和 / 平方和 | int sum | 數位和為 K 的數字個數 |
| 模餘數 | int mod | 能被 7 整除的數字個數 |
| 無重複數字 | int mask (bitmask) | 所有數字互不相同的數字個數 |
| 相鄰數字關係 | int last_digit | 相鄰數字差 ≥ 2 的數字個數 |
!tight && !lead 時使用 memo,避免狀態污染。digits[] 便於按位遍歷。class DigitDP {
vector digits;
long long memo[20][STATE_MAX];
long long dfs(int pos, bool tight, bool lead, int state) { ... }
public:
long long solve(long long x) {
digits.clear();
while (x) digits.push_back(x%10), x/=10;
memset(memo, -1, sizeof memo);
return dfs(digits.size()-1, true, true, INIT_STATE);
}
};
數位 DP 將「數字性質」轉化為「數位序列約束」,通過位級遞歸+記憶化實現指數級剪枝。掌握狀態抽象、邊界處理、優化技巧三大核心,即可攻克絕大多數計數類問題。