【题目描述】
由于科协里最近真的很流行数字游戏,某人又命名了一种取模数,这种数字必须满足各位数字之和 modN 为 0。现在大家又要玩游戏了,指定一个整数闭区间 [a,b],问这个区间内有多少个取模数。
【输入格式】
题目有多组测试数据。每组只含三个数字 a,b,N。
【输出格式】
对于每个测试数据输出一行,表示各位数字和 modN 为 0 的数的个数。
【样例输入】
1 19 9
【样例输出】
2
【数据范围与提示】
对于全部数据,1≤a,b≤2^31−1,1≤N<100
思路:这道题显然前面也有一个数字游戏,但是这一个要难很多,因为这道题可以用二维,但是我用的是三维更好理解。
f[i][j][k]表示以i为最高位的j位数的和为k
二维的就是 f[i][j]表示以i为最高位的j位数符合条件是有多少个
然后有了状态的定义,就简单很多了。我会给出三个代码,三维的原版详细注释,三维的精简版,和二维的版本
【代码实现:三维注释】
#include<cstdio>
#include<cstring>
using namespace std;
int f[20][20][110];//f[i][j][k]表示以i为最高位的j位数的各位数字之和为k的符合条件的数有多少个
int x,y,n;
int a[20],b[20],alen,blen;//a数组是用来存储x的数位,b数组是用来存储y的数位,alen表示x的最高位,blen表示y的最高位
void dfs()
{
for(int i=0;i<=9;i++) f[i][1][i]=1;//一位数的情况,以i为最高位的一位数的各位数之和为i,所有的一位数都是一种情况
for(int i=2;i<=10;i++)//数位的循环
{
for(int j=0;j<=9;j++)//从当前最高位开始循环
{
for(int k=0;k<=9;k++)//上一位数的最高位
{
for(int l=0;l<100;l++)//枚举和
{
f[j][i][j+l]+=f[k][i-1][l];//继承状态,上一位的和为l,然后当前一位的最高位为j,所以就加上最高位,就是和
}
}
}
}
}
int main()
{
dfs();
while(scanf("%d%d%d",&x,&y,&n)!=EOF)//多组数据
{
int kk=90/n;//2^31有十位数,所以如果都为9,最大和为90,kk表示的就是n的最大倍数
alen=0;//初始化
x--;//先将范围减少一个
while(x!=0)
{
a[++alen]=x%10;
x/=10;
}//这一步就是分出每一位 //把x的每一位分出来放到a数组里面
int sum1=0,t1=0;
for(int i=alen;i>=1;i--)//从高位开始枚举
{
for(int j=0;j<a[i];j++)//防止超出范围
{
for(int k=1;k<=kk;k++)//枚举最大倍数
{
sum1+=f[j][i][n*k-t1];//这里是枚举到了x-1,n*k一定是n的倍数
}
}
t1=(t1+a[i])%n;//数位的和mod n的结果,一边mod一边循环
}
if(t1==0 && alen!=0) sum1++;//如果x也成立,就增加一个
blen=0;//初始化
while(y!=0)
{
b[++blen]=y%10;
y/=10;
}//这一步就是分出每一位 //把y的每一位分出来放到b数组里面
int sum2=0,t2=0;
for(int i=blen;i>=1;i--)//从高位开始枚举
{
for(int j=0;j<b[i];j++)//防止超出范围
{
for(int k=1;k<=kk;k++)//枚举最大倍数
{
sum2+=f[j][i][n*k-t2];//这里是枚举到了y-1,保存答案
}
}
t2=(t2+b[i])%n;//数位的和mod n的结果,一边mod一边循环
}
if(t2==0 && blen!=0) sum2++;//如果y也成立,就增加一个
printf("%d\n",sum2-sum1);//x~y的范围的答案数
}
return 0;
}
【代码实现:三维精简】
#include<cstdio>
#include<cstring>
using namespace std;
int f[30][30][110],n,a[1100],k,x,y;
void dfs()
{
for(int i=0;i<=9;i++) f[1][i][i]=1;
for(int i=2;i<=20;i++)
{
for(int j=0;j<=9;j++)
{
for(int k=0;k<=9;k++)
{
for(int l=0;l<=100;l++)
{
f[i][j][l+j]+=f[i-1][k][l];
}
}
}
}
}
int solve(int x)
{
int len=0,ans=0,tmp=0;
while(x!=0)
{
a[++len]=x%10;
x/=10;
}
for(int i=len;i>=1;i--)
{
for(int j=0;j<a[i];j++)
for(int t=1;t<=k;t++)
ans+=f[i][j][n*t-tmp];
tmp=(tmp+a[i])%n;
}
if(tmp==0 && len!=0) ans++;
return ans;
}
int main()
{
dfs();
while(scanf("%d%d%d",&x,&y,&n)!=EOF)
{
k=100/n;
printf("%d\n",solve(y)-solve(x-1));
}
return 0;
}
【代码实现:二维版本】
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long LL;
LL f[110][110],mo,n,m;
LL dfs(LL x)
{
LL y=1,i=0,sum=0;
while(y<=x)
{
i++;
y*=10;
}
LL dfs1=0;
while(y!=1)
{
y/=10;i--;
for(LL j=0;j<=x/y-1;j++)
{
sum+=f[i][((dfs1-j)%mo+mo)%mo];
}
dfs1=((dfs1-x/y)%mo+mo)%mo;
x%=y;
}
if(dfs1==0) sum++;
return sum;
}
int main()
{
while(scanf("%lld%lld%lld",&n,&m,&mo)!=EOF)
{
memset(f,0,sizeof(f));
f[0][0]=1;
for(LL i=1;i<=10;i++)
{
for(LL j=0;j<=mo-1;j++)
{
for(LL k=0;k<=9;k++)
{
f[i][j]+=f[i-1][((j-k)%mo+mo)%mo];//加多一次mo是为了防止出现负数
}
}
}
printf("%lld\n",dfs(m)-dfs(n-1));
}
return 0;
}
整体还好,难度系数:7,因为要设计状态的定义