链接:https://www.nowcoder.com/acm/contest/163/J
题目描述
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].
示例1
输入
复制
2
10
18
输出
复制
Case 1: 10
Case 2: 12
【总结】
数位dp的模板题,但是状态转移方程怎么也没理解透,看完题解才恍然大悟。
通过此题,加深了对数位dp的理解。
【分析】
说白了就是dfs记忆化搜索,从高位向低位dfs,对整棵dfs树来说,有很多子树是一模一样的,因此记忆化剪枝。
对于一棵子树的状态,可以用祖先路径来记忆,当祖先路径上的数位和与余数一样时,由于子树都是一样的,所以这两个祖先路径分别衔接上这样的子树,对答案的贡献是相等的。
上面扯得可以忽略。
枚举每一个可能的位数和total,即1~9*pos
dp[i][j][r]:安排第i位时,前面i位的和为j,前i位构成的数字%total=r,该状态下,衔接上一个满的子树,对答案的贡献值。
【代码】
/****
***author: winter2121
****/
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define SI(i) scanf("%d",&i)
#define PI(i) printf("%d\n",i)
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int MAX=2e5+5;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
int dir[9][2]={0,1,0,-1,1,0,-1,0, -1,-1,-1,1,1,-1,1,1};
template<class T>bool gmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>bool gmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>void gmod(T &a,T b){a=(a%mod+b)%mod;}
int bit[20];
ll dp[14][110][110]; //位,sum,%
ll dfs(int pos,int sum,int remain,bool limit,int total)
{
if(pos<0)return sum==total&&remain==0; //总和等于枚举的那个和total
if(!limit&&dp[pos][sum][remain]!=-1)return dp[pos][sum][remain];
int up=limit?bit[pos]:9;
ll res=0;
for(int i=0;i<=up;i++)
{
if(sum+i>total)break;
res+=dfs(pos-1,sum+i,(remain*10+i)%total,limit&&i==up,total);
}
if(!limit) return dp[pos][sum][remain]=res;
return res;
}
ll solve(ll x)
{
int pos=0;
for(;x;x/=10)bit[pos++]=x%10;
ll ans=0;
for(int i=1;i<=9*pos;i++)
{
memset(dp,-1,sizeof(dp));
ans+=dfs(pos-1,0,0,1,i);
}
return ans;
}
int main()
{
int T;
cin>>T;
rep(cas,1,T)
{
ll n,ans=0;
scanf("%lld",&n);
printf("Case %d: %lld\n",cas,solve(n));
}
return 0;
}