这道题的意思非常简单,就是一个数,如果他除以他各个位数之和为0,即他能被他的位数之和整除,那么他就是要求的目标数,给一定一个n,问从0到n有多少目标数。
一看就是数位dp,不过我不会用递推写,只能借助于记忆搜索了。代码都有注释。
注意不用记忆化搜索的话会超时,
状态转移方程
dp[len][asum][cur][mod]+=dp[len-1][asum-i][cur][mod];
具体看代码,我说的不够好。不过大家应该能看懂,注释挺多的。
矩阵里从来不是一个数占一个位置,不然会超时。
他是对数的性质形成了一个巨大的矩阵集合,矩阵里是值,仅此而已。
最多12位,所以除数最多 12*9=108;
关键矩阵词:处理长度,总和,除数,余数。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int maxn=15;
int bit[maxn];//用来记录位数
long long dp[15][110][110][120];
long long DP(int len,int asum,int m,int div,bool e)
{ //len 是当前处理的长度;asum是处理前每位相加的和。
// m是当前的余数,余数等于0代表除尽,符合目标;
// 因为是从高位到地位一位一位处理的,所以要慢慢来
//e是对边界的处理,残留的记忆就是对边界的处理
if(len==0) return (asum==div)&&(m==0);//满足条件且余数相除为0;
int u=e?bit[len]:9;//如果在边界,就调用
if(dp[len][asum][m][div]!=-1&&!e)
return dp[len][asum][m][div];
long long res=0;
for(int i=0;i<=u;i++)
{ res+=DP(len-1,asum+i,(m*10+i)%div,div,e&&i==u);
}
return e?res:dp[len][asum][m][div]=res;
//return res;
}
void solve(long long t)
{ //int sum=0;
int l=1;
while(t>9)
{ bit[l++]=t%10;
t/=10;
}
bit[l]=t;
//for(int i=1;i<=l;i++)
//printf("%d\n",bit[i]);
long long res=0;
for(int i=1;i<=108;i++)
res+=DP(l,0,0,i,1);
cout<<res<<endl;
}
int main()
{ freopen("just.in","r",stdin);
freopen("just.out","w",stdout);
long long m;
memset(dp,-1,sizeof(dp));
scanf("%lld",&m);
solve(m);
return 0;
}