#10166. 「一本通 5.3 练习 1」数字游戏

【题目描述】

由于科协里最近真的很流行数字游戏,某人又命名了一种取模数,这种数字必须满足各位数字之和 modN 为 000。现在大家又要玩游戏了,指定一个整数闭区间 [a,b][a,b][a,b],问这个区间内有多少个取模数。

【输入格式】

题目有多组测试数据。每组只含三个数字 a,b,Na, b, Na,b,N。

【输出格式】

对于每个测试数据输出一行,表示各位数字和 modN 为 000 的数的个数。

【样例输入】

1 19 9

【样例输出】

2

【数据范围与提示】

对于全部数据,1≤a,b≤231−1,1≤N<1001\le a,b\le 2^{31}-1,1\le N\lt 1001≤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,因为要设计状态的定义 

猜你喜欢

转载自blog.csdn.net/qq_42367531/article/details/82557944