标签:memset 初始 ace 数位dp max out 一个 而且 记忆化搜索
# include <iostream>
# include <cstdio>
# include <cstring>
# define LL long long
# define MAXN 22
using namespace std;
int sum, a[MAXN];
// sum 记录各个位数的和
// a[0]记录当前数的总位数, 后面依次记录位
LL f[MAXN][MAXN*9][MAXN*9];
// f[dep][cur][mod] -> 当前 dp 到了第 dep 位, 现在该数的各位之和为 cur, mod 为 % 当前情况的sum值
LL DFS(int dep, int cur, int mod, bool eq){
if(cur > sum) return 0; // 当总和比当前统计的情况的和还大说明不符合当前的情况
if(!dep) return mod == 0 && cur == sum; // 如果当前各位数之和与要求的各位数之和相等, 且 mod=0, 就是一个符合条件的答案
if((!eq)&&(~f[dep][cur][mod])) return f[dep][cur][mod]; // 如果当前不是恰好是答案的询问就可以套记忆化搜索
// 注意记忆化搜索得到的值都是按照每位的范围为 0~9 计算的, 拿来更新 eq 为真的值就 boom 了
// 若当前的值没有超出边界而且第一次被搜索到
int lim = (eq? a[dep] : 9); // 确定下一位可以选择的最大值
LL ans = 0;
for(int i = 0; i <= lim; i++) // 搜索下一位的数位情况, 注意下一位数位从 0 开始
ans += DFS(dep-1, cur+i, (mod*10+i)%sum, eq&&(i==lim));
if(!eq) // 当前的答案可以用于更新记忆化搜索的值
f[dep][cur][mod] = ans;
return ans;
}
// dep -> 当前所在的位数
// cur -> 当前的数位之和
// mod -> 当前的数 %sum 的值
// eq -> 记录上一位填入的数是否和 a[] 中的数相等
LL DP(LL x){
a[0] = 0;
for(LL i = x; i; i/=10)
a[++a[0]] = i%10; // 预处理 a[i] 数组
LL ans = 0;
for(int i = 1; i <= a[0]*9; i++){ // 各个位数之和一定不超过 位数*9
sum = i; // 统计位数和为 i 的情况
memset(f, -1, sizeof(f)); // 初始化 f 为全 1
ans += DFS(a[0], 0, 0, 1); // DFS 过程
}
return ans;
}
int main(){
LL l, r;
cin>>l>>r;
cout<<DP(r) - DP(l-1);
return 0;
}
[数位DP][AHOI2009] Luogu P4127 同类分布
标签:memset 初始 ace 数位dp max out 一个 而且 记忆化搜索
原文地址:https://www.cnblogs.com/Foggy-Forest/p/13280575.html