题意:求小于等于N且能被自己所有位上数之和整除的数的个数。
分析:裸的数位dp。用一个三位数组dp[i][j][k]记录:第i位,之前数位之和为j,对某个mod余数为k的状态下满足条件的个数。这里mod的值就是小于等于N的数中,所有可能出现的数位之和。所以solve函数中需要对dfs函数做一个循环,上限是9*pos(数位之和不会超过9*pos)。
还有需要注意的是,在递归的时候可以通过判断当前数位之和sum是否超过mod,超过的话肯定在这个状态下没有满足条件的数,以此剪枝优化。
#include<bits/stdc++.h> using namespace std; const int maxn=3e5+5; const int INF =0x3f3f3f3f; typedef long long LL; int a[20]; LL dp[13][105][150]; //dp[i][j][k]记录 第i位,前面数位之和为j,对某个mod的余数是k的状态下满足条件的个数 int mod; LL dfs(int pos,int sum,int remain,bool limit){ if(pos==-1) return (sum==mod && !remain); if(dp[pos][sum][remain]!=-1 && !limit) return dp[pos][sum][remain]; int up = limit? a[pos]:9; LL res=0; for(int i=0;i<=up;++i){ if(i+sum>mod) break; //剪枝 res+=dfs(pos-1,sum+i,(remain*10+i)%mod,limit && i==a[pos]); } if(!limit) dp[pos][sum][remain] = res; return res; } LL solve(LL N) { int pos=0; LL x=N; while(x){ a[pos++]=x%10; x/=10; } LL res=0; for(int i=1;i<=9*pos;++i){ mod=i; memset(dp,-1,sizeof(dp)); //这里的dp数组记录的只是针对一种模数的状态,所以每次都要清空 res+=dfs(pos-1,0,0,true); } return res; } int main(){ int T,M,num,t,x; LL N; #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif while(scanf("%lld",&N)==1){ printf("%lld\n",solve(N)); } return 0; }