题目链接:
https://acm.hdu.edu.cn/showproblem.php?pid=4389
题目大意:
给出一个函数
int f ( int x ) {
if ( x == 0 ) return 0;
return f ( x / 10 ) + x % 10;
}
组测试,每组给两个数 和 ,求出在 范围中的数 ,满足 % == 0 这个等式的有多少个。
思路:
函数的返回值就是 所有位置上的数字和,显然,通过暴力枚举区间内每个数去计算满足条件的数字数量的时间复杂度太高。尝试用其它的方法来求解。
所有位数之和最大只有 81,所以考虑枚举所有位数之和来。
定义一个四维的 数组 ,表示枚举到第 位,所有位数之和为 ,对 取模,余数为 的数有多少个。通过记忆化搜索的方式求值。
在搜索的过程中,要考虑每一位上的值有没有限制,如果前面都相等,例如求的数是 1234,枚举到 122X,那么 X 可以从 0 取到 9,如果是 123X,那 X 最大只能到 4 了。所以 的过程中还要再多一个参数,记录当前求的这一个状态有没有限制。
刚开始枚举的是最高位,从最高位依次枚举到最低位,结束的状态就是当枚举完每一位,若位数之和就是取模的数,且余数为 0,说明取模之后结果为 0,那答案就 +1,否则不加。
当某个状态已经被记录了,那么直接返回该值,不再重复计算,降低时间复杂度。
从某个状态转移到下一个状态, 要加上这个数,取模不变,余数从 变成了 % ,即加入新的数之后取模留下的余数。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 11, M = 82;
int T, a, b, d[N], dp[N][M][M][M];
int dfs(int pos, int sum, int mod, int res, bool f){
if (pos == 0){
if (sum == mod && res == 0) return 1;
else return 0;
}
if (!f && dp[pos][sum][mod][res] != -1) return dp[pos][sum][mod][res];
int ans = 0, c = !f ? 9 : d[pos];
for (int i = 0; i <= c; i ++ )
ans += dfs(pos - 1, sum + i, mod, (res * 10 + i) % mod, f && i == d[pos]);
if (!f) dp[pos][sum][mod][res] = ans;
return ans;
}
int solve(int x){
int len = 0;
while (x){
d[++len] = x % 10;
x /= 10;
}
int ans = 0;
for (int i = 1; i <= 81; i ++ )
ans += dfs(len, 0, i, 0, true);
return ans;
}
int main(){
memset(dp, -1, sizeof dp);
cin >> T;
for (int i = 1; i <= T; i ++ ){
cin >> a >> b;
printf("Case %d: %d\n", i, solve(b) - solve(a - 1));
}
return 0;
}
分类:
动态规划-数位dp
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通