题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4389
题目大意:
求给定区间内,各位数字之和能被该数整除的数的个数。
解题思路:
数位dp。
注意用记忆化搜索做的时候,要保证记忆的状态能够重用并且不冲突,重要的是唯一确定性。
dp[i][j][k][l]表示后面还有i位,前面所有位数和为j,前面各位组成的数对l求模的余数为k,满足题目要求的数的种数。
本题的关键是,加一维mod数,使求余的数固定,以缩小存储的范围,同时满足唯一确定性,即可重用。
代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<stack> #include<list> #include<queue> #define eps 1e-6 #define INF (1<<30) #define PI acos(-1.0) using namespace std; int dp[10][82][82][82]; int pos[15]; int dfs(int cur,int lastf,int lastx,int mod,int flag) { if(!cur) { if(lastx==0&&lastf==mod) return 1; return 0; } if(!flag&&dp[cur][lastf][lastx][mod]!=-1) return dp[cur][lastf][lastx][mod]; int Max=flag?pos[cur]:9; int ans=0; for(int i=0;i<=Max;i++) { ans+=dfs(cur-1,lastf+i,(lastx*10+i)%mod,mod,flag&&i==Max); } if(!flag) dp[cur][lastf][lastx][mod]=ans; return ans; } int Cal(int n) { int tt=0; while(n) { ++tt; pos[tt]=n%10; n/=10; } int temp=0; for(int i=1;i<=81;i++) temp+=dfs(tt,0,0,i,1); //把各位数之和为i的满足题意数的个数全部找出来,简化的关键 return temp; } int main() { int t,a,b; scanf("%d",&t); memset(dp,-1,sizeof(dp)); for(int ca=1;ca<=t;ca++) { scanf("%d%d",&a,&b); printf("Case %d: %d\n",ca,Cal(b)-Cal(a-1)); } return 0; }