例4. 不超过N的数字组合
本题非常简单,只是枚举每个数字是限定在 digits
数组中,而不是$0~9$。 数位之间的数字也没有限制条件。
#include<bits/stdc++.h>
using namespace std;
const int N = 31;
int memo[N];
int n, m;
string s, digits;
int dfs(int i, bool lim, bool ok) {
if (i == m) return ok; // 如果填了数字,则为 1 种合法方案
if (!lim && ok && memo[i] >= 0) return memo[i]; // 在不受到任何约束的情况下,返回记录的结果,避免重复运算
int ret = 0;
// 前面不填数字,那么可以跳过当前数位,也不填数字
// lim 改为 false,因为没有填数字,位数都比 n 要短,自然不会受到 n 的约束
// ok 仍然为 false,因为没有填任何数字
if (!ok) ret = dfs(i + 1, false, false);
char up = lim ? s[i] : '9'; // 根据是否受到约束,决定可以填的数字的上限
// 注意:对于一般的题目而言,如果这里 is_num 为 false,则必须从 1 开始枚举,由于本题 digits 没有 0,所以无需处理这种情况
for (auto &d : digits) { // 枚举要填入的数字 d
if (d > up) break; // d 超过上限,由于 digits 是有序的,后面的 d 都会超过上限,故退出循环
// is_limit:如果当前受到 n 的约束,且填的数字等于上限,那么后面仍然会受到 n 的约束
// is_num 为 true,因为填了数字
ret += dfs(i + 1, lim && (d == up), true);
}
if (!lim && ok) memo[i] = ret; // 在不受到任何约束的情况下,记录结果
return ret;
}
int main(){
cin >> digits;
cin >> n;
s = to_string(n);
m = s.size();
memset(memo, -1, sizeof memo);
int ans = dfs(0, true, false); //首位受限制
cout << ans << '\n';
return 0;
}