数论之模加法运算

问题

一个 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]);
    }
}

你可能感兴趣的:(数据结构与算法)