数论之模加法运算

问题

一个 m 位的整数,有多少可以被整数n整除?其中 m 可以大于15。

  • 测试输入
    1 3
  • 测试输出
    4

分析1

这个问题显然不能穷举所有的情况。数论中的模加法运算有这样的性质(对乘法也是一样):

(a+b) mod n===(a mod n+b mod n) mod n(a mod n+b) mod n(a+b mod n) mod n

由此可见,一个数的取模问题可以拆分成两个求和数的取模问题,这显然属于动态规划能够解决的问题。对于一个 m 位的整数x,可以将其按照十进制拆解为 x=x0×10m1+x1×10m2++xm2×10+xm1 。对于其中的 i1 i 位做如下分析:
j=xi1 mod n,则

(xi1×10+xi) mod n====[(xi1×10) mod n+xi] mod n{[(xi1 mod n)×10] mod n+xi} mod n[(j×10) mod n+xi] mod n(j×10+xi) mod n

由此定义 c[i,j] 为前 i 位数模n余数为 j 的个数,原问题为c[m,0]。第 i 位的取值k=09,穷举一遍后前 i 位余数为(j10+k)%n的个数,等于前 i1 位余数为 j 的个数的累计和。从而递归式为

{c[0,0]=1,c[i,j]=0,c[i,(j10+k)%n] +=c[i1,j],i=0,j0i0,k=09

代码1

import java.util.Scanner;

public class ModNums {
    static void solution(long[][] c, int m, int n) {
        for (int i = 0; i < n; i++) {
            c[0][i] = 0L;
        }
        c[0][0] = 1L;
        for (int i = 1; i <= m; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < 10; k++) {
                    c[i][(j * 10 + k) % n] += c[i - 1][j];
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();
        long[][] c = new long[m + 1][n];
        solution(c, m, n);
        System.out.println(c[m][0]);
    }
}

变形

一个 m 位的整数,其中有某些位不确定,用”X”表示,有多少可以被整数n整除?

  • 测试输入
    999999999X 3
  • 测试输出
    4

分析2

整体和原问题一样,只是不确定的位才需要穷举一遍,确定的位直接更新即可。

代码2

import java.util.Scanner;

public class AmbiguousNums {
    static void solution(long[][] c, String seq, int n) {
        int length = seq.length();
        for (int i = 0; i < n; i++) {
            c[0][i] = 0L;
        }
        c[0][0] = 1L;
        for (int i = 1; i <= length; i++) {
            char ch = seq.charAt(i - 1);
            for (int j = 0; j < n; j++) {
                if (ch == 'X') {
                    for (int k = 0; k < 10; k++) {
                        c[i][(j * 10 + k) % n] += c[i - 1][j];
                    }
                } else {
                    int k = ch - '0';
                    c[i][(j * 10 + k) % n] += c[i - 1][j];
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String seq = sc.next();
        int length = seq.length();
        int n = sc.nextInt();
        long[][] c = new long[length + 1][n];
        solution(c, seq, n);
        System.out.println(c[length][0]);
    }
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值