随笔 - 216,  文章 - 0,  评论 - 17,  阅读 - 25424

题目链接:

https://acm.hdu.edu.cn/showproblem.php?pid=4389

题目大意:

给出一个函数 f

int f ( int x ) {
   if ( x == 0 ) return 0;
   return f ( x / 10 ) + x % 10;
}

T 组测试,每组给两个数 ab,求出在 [a,b] 范围中的数 x,满足 x % f(x) == 0 这个等式的有多少个。

思路:

f(x) 函数的返回值就是 x 所有位置上的数字和,显然,通过暴力枚举区间内每个数去计算满足条件的数字数量的时间复杂度太高。尝试用其它的方法来求解。
所有位数之和最大只有 81,所以考虑枚举所有位数之和来。
定义一个四维的 dp 数组 dp[pos][sum][mod][res],表示枚举到第 pos 位,所有位数之和为 sum,对 mod 取模,余数为 res 的数有多少个。通过记忆化搜索的方式求值。
在搜索的过程中,要考虑每一位上的值有没有限制,如果前面都相等,例如求的数是 1234,枚举到 122X,那么 X 可以从 0 取到 9,如果是 123X,那 X 最大只能到 4 了。所以 dfs 的过程中还要再多一个参数,记录当前求的这一个状态有没有限制。
刚开始枚举的是最高位,从最高位依次枚举到最低位,结束的状态就是当枚举完每一位,若位数之和就是取模的数,且余数为 0,说明取模之后结果为 0,那答案就 +1,否则不加。
当某个状态已经被记录了,那么直接返回该值,不再重复计算,降低时间复杂度。
从某个状态转移到下一个状态,sum 要加上这个数,取模不变,余数从 res 变成了 (res10+i) % mod,即加入新的数之后取模留下的余数。

代码:

#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;
}
posted on   Hamine  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示