资料链接:
入门ppt
link1:记忆化方式
link2
link3
论文1
论文2
//=====================//
记忆化的数位dp:
通常而言,有四个参数必须 dp( pos, flag, limit )
pos表示当前正在枚举的数位。
flag标志已经枚举的前缀是否某种性质(前面的数位和,是否含有某个数,前一个枚举的数等等。。),当然flag可以有多个。
limit表示当前是否为上限,有时还会记录是否有前导0。
相较而言,记忆化搜索更容易编写,需要维护的细节更少。
假设要对数位中没有5的数计数。
int digit[Maxn], dp[Maxn][flag];
// 在程序最开始初始化一次即可(假设有多个区间需要询问),想想为什么
void init() {
memset(dp, -1, sizeof(dp));
}
// flag 是否没有5
int go (int pos, int limit, int flag) {
if (pos < 0) {
return flag == 1;
}
if (limit || dp[pos][flag] == -1) {
int tmp = 0;
int last = limit ? digit[pos] : 9;
for (int i=0;i<=last;++i)
tmp += go (pos - 1, limit && (i == last), flag && (i != 5));
if (limit) return tmp; // 上界要单独计算,不能存状态
dp[pos][flag] = tmp;
}
return dp[pos][flag];
}
// count [1, x]
int Count(int x) {
int len = 0;
while (x) {
digit[len ++] = x % 10; x /= 10;
}
return go (0,
}