AcWing 1084 数字游戏 II

题目描述:

由于科协里最近真的很流行数字游戏。

某人又命名了一种取模数,这种数字必须满足各位数字之和 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;
}
发布了311 篇原创文章 · 获赞 31 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/104501801