题目描述:
由于科协里最近真的很流行数字游戏。
某人又命名了一种取模数,这种数字必须满足各位数字之和 mod N为 0。
现在大家又要玩游戏了,指定一个整数闭区间 [a.b],问这个区间内有多少个取模数。
输入格式
输入包含多组测试数据,每组数据占一行。
每组数据包含三个整数 a,b,N。
输出格式
对于每个测试数据输出一行结果,表示区间内各位数字和 mod N为 0 的数的个数。
数据范围
1≤a,b≤2^31−1,
1≤N<100
输入样例:
1 19 9
输出样例:
2
分析:
方法一:动态规划
首先如果用f[i][j]表示一个以j开头的i位数中各位数字之和能被N整除的数的个数的话,则不太好从f[i-1][k]递推过来,事实上,本题的条件是mod N为0,则所有的数根据各位之和模上N结果的不同被划分为N个不同的等价类。所以可以再加上一维表示各位之和模上N的结果,即f[i][j][k]表示一个以j开头的i位数中各位数字之和模上N等于k的数的个数。则状态转移方程为f[i][j][k] += f[i-1][t][s],其中t表示对第i-1位上数的枚举,(s + j) mod N == k,即s = (k - j) % N,由于k-j可能是负数,所以需要令s = (k - j % N + N) % N将控制在0到N-1之间。后面每位的枚举过程和之前的题目类似,如果当前位置上的数小于n对应位置上的数,则方案数直接加上f[i+1][j][(mod-last) % mod],last表示前面位置上的数之和模上mod的数(mod就是前面提到的N),则为了这个数模上mod为0,以j开头剩下位置上的数模上mod应该就是(mod-last) % mod了。枚举到最后一位如果last恰好为0,res++。实现的细节见代码:
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 12;
int f[N][N][100],mod;
void init(){
for(int i = 0;i <= 9;i++) f[1][i][i % mod]++;
for(int i = 2;i < N;i++){
for(int j = 0;j <= 9;j++){
for(int k = 0;k < mod;k++){
for(int t = 0;t <= 9;t++){
int s = (k - j % mod + mod) % mod;
f[i][j][k] += f[i-1][t][s];
}
}
}
}
}
int get(int n){
if(!n) return 1;
vector<int> num;
while(n) num.push_back(n % 10),n /= 10;
int res = 0,last = 0;
for(int i = num.size() - 1;i >= 0;i--){
int x = num[i];
for(int j = 0;j < x;j++) res += f[i + 1][j][(mod - last) % mod];
last = (last + x) % mod;
if(!i && !last) res++;
}
return res;
}
int main(){
int a,b;
while(cin>>a>>b>>mod){
memset(f,0,sizeof f);
init();
cout<<get(b) - get(a - 1)<<endl;
}
return 0;
}
方法二:记忆化搜索
这题记忆化搜索相对于动态规划的优势还是比较明显的,速度差不多快了四倍。首先依旧考虑dfs的参数,当前枚举到的位置u,以及前面枚举到的数字是否已经小于n对应数字的标志位flag,本题新增一个参数是已经枚举的数字之和模上mod的结果s,可以用s决定最终递归基的走向。dfs的过程只是将前几题的框架稍微改动了下,较为简单。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 12;
int mod,len,num[N],f[N][100][2];
int dfs(int u,int s,int flag){
if(f[u][s][flag] != -1) return f[u][s][flag];
if(!u){
if(s) return f[u][s][flag] = 0;
else return f[u][s][flag] = 1;
}
int res = 0;
for(int i = 0;i <= 9;i++){
int t = (s + i) % mod;
if(flag && i >= num[u]){
if(i == num[u]) res += dfs(u - 1,t,1);
break;
}
else res += dfs(u - 1,t,0);
}
return f[u][s][flag] = res;
}
int get(int n){
if(!n) return 1;
len = 0;
while(n) num[++len] = n % 10,n /= 10;
memset(f,-1,sizeof f);
return dfs(len,0,1);
}
int main(){
int a,b;
while(cin>>a>>b>>mod){
cout<<get(b) - get(a - 1)<<endl;
}
return 0;
}