【数位dp】Beautiful Numbers @2018acm上海大都会赛J
Beautiful Numbers
PROBLEM
题目描述
NIBGNAUK is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by the sum of its digits.
We will not argue with this and just count the quantity of beautiful numbers from 1 to N.
输入描述:
The first line of the input is T(1≤ T ≤ 100), which stands for the number of test cases you need to solve.
Each test case contains a line with a positive integer N (1 ≤ N ≤ 1012).
输出描述:
For each test case, print the case number and the quantity of beautiful numbers in [1, N].
输入
2
10
18
输出
Case 1: 10
Case 2: 12
MEANING
t组测试用例,每组测试用例给一个整数n,询问从1到n中有多少数能被其各位数之和整除。
SOLUTION
对于每个整数n,找到从1到n的每个数的各位数和的最大值k。
从1到k枚举各位数之和。
对于每个各位数之和,相当于在1到n中找有多少数能整除这个和,且各位数之和等于这个和。这样就将问题分解成若干子问题。
接下来就是数位dp套板子。具体可以看代码中的注释。
CODE
#define IN_PC() freopen("C:\\Users\\hz\\Desktop\\in.txt","r",stdin)
#define IN_TEST() freopen("","r",stdin)
#define IN_LB() freopen("C:\\Users\\acm2018\\Desktop\\in.txt","r",stdin)
#define OUT_PC() freopen("C:\\Users\\hz\\Desktop\\out.txt","w",stdout)
#define OUT_LB() freopen("C:\\Users\\acm2018\\Desktop\\out.txt","w",stdout)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1010;
ll dp[15][100][100];
bool q[15][100][100];
int b[15], tot;
//pos:枚举到哪一位 sum:最高位到当前位的和 remain:当前枚举的数除以各位数和的余数
ll dfs(int pos, int sum, int remain, int meiju, bool limit) {
if(pos == 0) return (remain == 0) ? 1ll : 0;
if(limit && q[pos][sum][remain]) return dp[pos][sum][remain];//记忆化搜索
ll res = 0;
int up = limit?9:b[pos];
for(int i = 0; i <= up; i++) {
if(i + sum <= meiju && i + sum + 9 * (pos - 1) >= meiju)
res += dfs(pos-1, sum+i, (remain*10+i) % meiju, meiju, limit || (i!=b[pos]));
}
if(limit) {
q[pos][sum][remain] = true;
dp[pos][sum][remain] = res;
}
return res;
}
ll solve(ll num) {
int sum = 0;
tot = 0;
while(num) {
sum += num % 10;
b[++tot] = num % 10;
num /= 10;
}
int ret;
if(tot == 1)ret = b[tot];
else ret = b[tot] - 1 + (tot - 1) * 9;
sum = max(sum, ret);//找到各位数之和的最大值
ll ans = 0;
for(int i = 1; i <= sum; i++) {//枚举各位数之和,解决子问题
memset(dp, 0, sizeof dp);
memset(q, 0, sizeof q);
ans += dfs(tot, 0, 0, i, false);
}
return ans;
}
int main() {
// IN_PC();
int T;
scanf("%d", &T);
for(int ca = 1; ca <= T; ca++) {
ll n;
scanf("%lld", &n);
printf("Case %d: ", ca);
printf("%lld\n", solve(n));
}
return 0;
}