[编程题]双核处理
一种双核CPU的两个核能够同时的处理任务,现在有n个已知数据量的任务需要交给CPU处理,假设已知CPU的每个核1秒可以处理1kb,每个核同时只能处理一项任务。n个任务可以按照任意顺序放入CPU进行处理,现在需要设计一个方案让CPU处理完这批任务所需的时间最少,求这个最小的时间。
输入描述:
输入包括两行: 第一行为整数n(1 ≤ n ≤ 50) 第二行为n个整数length[i](1024 ≤ length[i] ≤ 4194304),表示每个任务的长度为length[i]kb,每个数均为1024的倍数。
输出描述:
输出一个整数,表示最少需要处理的时间
示例1
输入
5 3072 3072 7168 3072 1024
输出
9216
0-1背包问题,时间接近总的工作时间的一半最好,所以背包容量是总时间一半,用贪心算法只能通过50%
不正确的贪心:
import java.util.*;
public class Main {
public static void main(String[] args) {
doubleCPU();
}
public static void doubleCPU() {
Scanner scanner = new Scanner(System.in);
int worksum = scanner.nextInt();
int[] works = new int[worksum];
for (int i = 0; i < worksum; i++) {
works[i] = scanner.nextInt();
}
// 排序
Arrays.sort(works);
ArrayList<Integer> cpu1 = new ArrayList<>();
ArrayList<Integer> cpu2 = new ArrayList<>();
//如果只有一项工作
if(worksum==1){
System.out.println(works[0]);
return;
}
//两项工作,取其中大的工作
if(worksum==2){
System.out.println(Math.max(works[0], works[1]));
return;
}
//两项以上的工作
if (worksum >= 2) {
//cpu1添加最大的工作,cpu2添加次大的工作
cpu1.add(works[worksum - 1]);
cpu2.add(works[worksum - 2]);
for(int i=worksum-3;i>=0;i--){
if(minCPU(cpu1)<minCPU(cpu2)){
cpu1.add(works[i]);
}else{
cpu2.add(works[i]);
}
}
}
System.out.println(Math.max(minCPU(cpu1), minCPU(cpu2)));
}
// 判断每个cpu工作量的方法
public static int minCPU(ArrayList<Integer> cpu) {
int CPU = 0;
for (int i = 0; i < cpu.size(); i++) {
CPU += cpu.get(i);
}
return CPU;
}
}
正确的0-1背包:、
// 0-1背包,任务时间接近总时间的一半最好
public static void doubleCPU2() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int n = scanner.nextInt();
int[] arr = new int[n];
int sum = 0;
for (int i = 0; i < arr.length; i++) {
arr[i] = scanner.nextInt() >> 10;
sum += arr[i];
}
// dp[j]表示在容量为j的情况下可存放的重量
// 如果不放arr[i]重量为dp[j],如果放arr[i]重量为dp[j-arr[i]]+arr[i];
int[] dp = new int[sum / 2 + 1];
for (int i = 0; i < n; i++) {
for (int j = sum / 2; j >= arr[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - arr[i]] + arr[i]);
}
}
System.out.println(Math.max(dp[sum / 2], sum - dp[sum / 2]) << 10);
}
}
[编程题]赶去公司
终于到周末啦!小易走在市区的街道上准备找朋友聚会,突然服务器发来警报,小易需要立即回公司修复这个紧急bug。假设市区是一个无限大的区域,每条街道假设坐标是(X,Y),小易当前在(0,0)街道,办公室在(gx,gy)街道上。小易周围有多个出租车打车点,小易赶去办公室有两种选择,一种就是走路去公司,另外一种就是走到一个出租车打车点,然后从打车点的位置坐出租车去公司。每次移动到相邻的街道(横向或者纵向)走路将会花费walkTime时间,打车将花费taxiTime时间。小易需要尽快赶到公司去,现在小易想知道他最快需要花费多少时间去公司。
输入描述:
输入数据包括五行:
第一行为周围出租车打车点的个数n(1 ≤ n ≤ 50)
第二行为每个出租车打车点的横坐标tX[i] (-10000 ≤ tX[i] ≤ 10000)
第三行为每个出租车打车点的纵坐标tY[i] (-10000 ≤ tY[i] ≤ 10000)
第四行为办公室坐标gx,gy(-10000 ≤ gx,gy ≤ 10000),以空格分隔
第五行为走路时间walkTime(1 ≤ walkTime ≤ 1000)和taxiTime(1 ≤ taxiTime ≤ 1000),以空格分隔
输出描述:
输出一个整数表示,小易最快能赶到办公室的时间
示例1
输入
2 -2 -2 0 -2 -4 -2 15 3
输出
42
简单题,全步行和穷举走到打车点再打车
import java.util.*;
public class Main {
public static void main(String[] args) {
goToCompany();
}
//打车去还是步行呢
public static void goToCompany(){
Scanner scanner=new Scanner(System.in);
int taxiSum=scanner.nextInt();
//打车点
int x[]=new int[taxiSum];
int y[]=new int[taxiSum];
for(int i=0;i<taxiSum;i++){
x[i]=scanner.nextInt();
}
for(int i=0;i<taxiSum;i++){
y[i]=scanner.nextInt();
}
//公司坐标
int x_company=scanner.nextInt();
int y_company=scanner.nextInt();
//步行时间和打车时间
int walk=scanner.nextInt();
int taxi=scanner.nextInt();
//走路去公司的时间
int minTime=(Math.abs(x_company)+Math.abs(y_company))*walk;
//步行去打车点打车去(遍历)
for(int i=0;i<taxiSum;i++){
int temp=0;
temp+=(Math.abs(x[i])+Math.abs(y[i]))*walk;
temp+=(Math.abs(x[i]-x_company)+Math.abs(y[i]-y_company))*taxi;
minTime=Math.min(temp, minTime);
}
System.out.println(minTime);
}
}
[编程题]调整队形
在幼儿园有n个小朋友排列为一个队伍,从左到右一个挨着一个编号为(0~n-1)。其中有一些是男生,有一些是女生,男生用'B'表示,女生用'G'表示。小朋友们都很顽皮,当一个男生挨着的是女生的时候就会发生矛盾。作为幼儿园的老师,你需要让男生挨着女生或者女生挨着男生的情况最少。你只能在原队形上进行调整,每次调整只能让相邻的两个小朋友交换位置,现在需要尽快完成队伍调整,你需要计算出最少需要调整多少次可以让上述情况最少。例如:
GGBBG -> GGBGB -> GGGBB
这样就使之前的两处男女相邻变为一处相邻,需要调整队形2次
输入描述:
输入数据包括一个长度为n且只包含G和B的字符串.n不超过50.
输出描述:
输出一个整数,表示最少需要的调整队伍的次数
示例1
输入
GGBBG
输出
2
最终都会变成前面都是女生后面男生,或者前面都是男生后面女生,比较这两种怎么挪移动次数少。
移动次数等于原来位置减去最后位置的绝对值。
import java.util.*;
public class Main {
public static void main(String[] args) {
//goToCompany();
paidui();
}
public static void paidui(){
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();
//最后都会变成GGGBB或者BBGGG,就看女生是在前面还是后面移动次数少
char[]array=str.toCharArray();
int boys=0,girls=0;
for(int i=0;i<array.length;i++){
if(array[i]=='G'){
girls++;
}else {
boys++;
}
}
//交换次数就是目标的位置减去原来位置的绝对值
//把女孩放在前面
int leftGirl=0;
int index=0,i=0;
while(i<girls){
if(array[index]=='G'){
leftGirl+=Math.abs(index-i);
i++;
}
index++;
}
//把女孩放在后面,即男孩在前面
int rightGirl=0;
index=0;i=0;
while(i<boys){
if(array[index]=='B'){
rightGirl+=Math.abs(index-i);
i++;
}
index++;
}
System.out.println(Math.min(leftGirl, rightGirl));
}
}
[编程题]消除重复元素
小易有一个长度为n序列,小易想移除掉里面的重复元素,但是小易想是对于每种元素保留最后出现的那个。小易遇到了困难,希望你来帮助他。
输入描述:
输入包括两行: 第一行为序列长度n(1 ≤ n ≤ 50) 第二行为n个数sequence[i](1 ≤ sequence[i] ≤ 1000),以空格分隔
输出描述:
输出消除重复元素之后的序列,以空格分隔,行末无空格
示例1
输入
9 100 100 100 99 99 99 100 100 100
输出
99 100
每个元素的最后一个加入,等于倒序的第一个加入,得到的结果再颠倒。
import java.util.*;
public class Main {
public static void main(String[] args) {
// goToCompany();
removeRepeat();
}
public static void removeRepeat() {
// 倒序的第一个加入,得到的结果再颠倒
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
List<Integer> list1 = new ArrayList<>();
for (int i = 0; i < num; i++) {
list1.add(scanner.nextInt());
}
List<Integer> list = new ArrayList<>();
for (int i = list1.size() - 1; i >= 0; i--) {
if (!list.contains(list1.get(i))) {
list.add(list1.get(i));
}
}
for (int i = list.size() - 1; i > 0; i--) {
System.out.print(list.get(i) + " ");
}
System.out.println(list.get(0));
}
}
[编程题]魔力手环
小易拥有一个拥有魔力的手环上面有n个数字(构成一个环),当这个魔力手环每次使用魔力的时候就会发生一种奇特的变化:每个数字会变成自己跟后面一个数字的和(最后一个数字的后面一个数字是第一个),一旦某个位置的数字大于等于100就马上对100取模(比如某个位置变为103,就会自动变为3).现在给出这个魔力手环的构成,请你计算出使用k次魔力之后魔力手环的状态。
输入描述:
输入数据包括两行: 第一行为两个整数n(2 ≤ n ≤ 50)和k(1 ≤ k ≤ 2000000000),以空格分隔 第二行为魔力手环初始的n个数,以空格分隔。范围都在0至99.
输出描述:
输出魔力手环使用k次之后的状态,以空格分隔,行末无空格。
示例1
输入
3 2 1 2 3
输出
8 9 7
这道题传统解法不难,后面再填一个首部构成环,但是超时了,还有更简单的做法后续学习,更简单的办法:矩阵快速幂,需要记住模版!!
import java.util.*;
public class Main {
public static void main(String[] args) {
magicShouhuan();
}
public static void magicShouhuan() {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int k=scanner.nextInt();
int []num=new int[n+1];
for(int i=0;i<n;i++){
num[i]=scanner.nextInt();
}
//首尾相连,后面再补一个第一位
num[n]=num[0];
for(int i=0;i<k;i++){
//前一位加后一位
for(int j=0;j<n;j++){
num[j]=num[j]+num[j+1];
while(num[j]>100){
num[j]=num[j]%100;
}
}
//首尾相连,后面再补一个第一位
num[n]=num[0];
}
for(int i=0;i<n-1;i++){
System.out.print(num[i]+" ");
}
System.out.println(num[n-1]);
}
}
矩阵快速幂:
static void juzhenkuaisumi(){
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int k=scanner.nextInt();
int [][]arr=new int[1][n];
for(int i=0;i<n;i++){
arr[0][i]=scanner.nextInt();
}
//初始化单元矩阵
int [][]mul=new int[n][n];
for(int i=0;i<n;i++){
if(i<n-1){
mul[i][i]=1;
mul[i+1][i]=1;
}else {
mul[i][i]=1;
mul[0][i]=1;
}
}
// for(int i=0;i<n;i++){
// for(int j=0;j<n;j++){
// System.out.print(mul[i][j]+" ");
// }
// System.out.println();
// }
for(;k>0;k>>=1){
if((k&1)==1){
arr=Core(arr, mul);
}
mul=Core(mul, mul);
}
//输出
for(int i=0;i<n-1;i++){
System.out.print(arr[0][i]+" ");
}
System.out.println(arr[0][n-1]);
}
static int[][] Core(int [][]a,int [][]b){
int rowRes=a.length;
int columnRes=b[0].length;
int columnA=a[0].length;// a[0].length == b.length;a的列数等于b的行数
int[][]c=new int [rowRes][columnRes];//结果矩阵行数等于a的行数,列数等于b的列数
for(int i=0;i<rowRes;i++){
for(int j=0;j<columnRes;j++){
for(int k=0;k<columnA;k++){
if(a[i][k]==0||b[k][j]==0){
continue;
}
c[i][j]+=a[i][k]*b[k][j];
}
if(c[i][j]>=100){
c[i][j]%=100;
}
}
}
return c;
}
[编程题]工作安排
现在有n位工程师和6项工作(编号为0至5),现在给出每个人能够胜任的工作序号表(用一个字符串表示,比如:045,表示某位工程师能够胜任0号,4号,5号工作)。现在需要进行工作安排,每位工程师只能被安排到自己能够胜任的工作当中去,两位工程师不能安排到同一项工作当中去。如果两种工作安排中有一个人被安排在的工作序号不一样就被视为不同的工作安排,现在需要计算出有多少种不同工作安排计划。
输入描述:
输入数据有n+1行: 第一行为工程师人数n(1 ≤ n ≤ 6) 接下来的n行,每行一个字符串表示第i(1 ≤ i ≤ n)个人能够胜任的工作(字符串不一定等长的)
输出描述:
输出一个整数,表示有多少种不同的工作安排方案
示例1
输入
6 012345 012345 012345 012345 012345 012345
输出
720
用到了回溯法(重点),n皇后问题需要回溯法,多看看这道题
回溯法用递归实现,注意递归的出口
import java.util.*;
public class Main {
public static void main(String[] args) {
work();
}
private static int result = 0;
public static void work() {
// 回溯法
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
String[] array = new String[n];
for (int i = 0; i < n; i++) {
array[i] = scanner.next();
}
backtracing(array, 0, new HashSet<>());
System.out.println(result);
}
private static void backtracing(String[] array, int index, HashSet set) {
if (index >= array.length) {
result++;
//System.out.println("array,length = " + array.length);
return;
}
String arraynew = array[index];
for (int i = 0; i < arraynew.length(); i++) {
int work = arraynew.charAt(i) - '0';
if (!set.contains(work)) {
set.add(work);
backtracing(array, index + 1, set);
set.remove(work);
}
}
}
}
[编程题]集合
小易最近在数学课上学习到了集合的概念,集合有三个特征:1.确定性 2.互异性 3.无序性.
小易的老师给了小易这样一个集合:
S = { p/q | w ≤ p ≤ x, y ≤ q ≤ z }
需要根据给定的w,x,y,z,求出集合中一共有多少个元素。小易才学习了集合还解决不了这个复杂的问题,需要你来帮助他。
输入描述:
输入包括一行: 一共4个整数分别是w(1 ≤ w ≤ x),x(1 ≤ x ≤ 100),y(1 ≤ y ≤ z),z(1 ≤ z ≤ 100).以空格分隔
输出描述:
输出集合中元素的个数
示例1
输入
1 10 1 1
输出
10
简单题,集合用set,set能去重
import java.util.*;
public class Main {
public static void main(String[] args) {
jihe();
}
public static void jihe() {
Scanner scanner=new Scanner(System.in);
int w=scanner.nextInt();
int x=scanner.nextInt();
int y=scanner.nextInt();
int z=scanner.nextInt();
HashSet<Double>set=new HashSet<>();
for(int p=w;p<=x;p++){
for(int q=y;q<=z;q++){
if(q!=0){
set.add(p*1.0/q);
}
}
}
System.out.println(set.size());
}
}
[编程题]奇怪的表达式求值
常规的表达式求值,我们都会根据计算的优先级来计算。比如*/的优先级就高于+-。但是小易所生活的世界的表达式规则很简单,从左往右依次计算即可,而且小易所在的世界没有除法,意味着表达式中没有/,只有(+, - 和 *)。现在给出一个表达式,需要你帮忙计算出小易所在的世界这个表达式的值为多少
输入描述:
输入为一行字符串,即一个表达式。其中运算符只有-,+,*。参与计算的数字只有0~9. 保证表达式都是合法的,排列规则如样例所示。
输出描述:
输出一个数,即表达式的值
示例1
输入
3+5*7
输出
56
简单题,从左向右计算,注意char转int是用char变量-'0'
import java.util.*;
public class Main {
public static void main(String[] args) {
jisuan();
}
public static void jisuan() {
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
char[] arr = str.toCharArray();
int result=arr[0]-'0',i=0;
while(i<arr.length-2){
result=calculate(result, arr[i+1], arr[i+2]-'0');
i=i+2;
}
System.out.println(result);
}
// 计算
public static int calculate(int aint, char b, int cint) {
int result = 0;
switch (b) {
case '+':
result = aint + cint;
break;
case '-':
result = aint - cint;
break;
case '*':
result = aint * cint;
break;
}
return result;
}
}
[编程题]涂棋盘
小易有一块n*n的棋盘,棋盘的每一个格子都为黑色或者白色,小易现在要用他喜欢的红色去涂画棋盘。小易会找出棋盘中某一列中拥有相同颜色的最大的区域去涂画,帮助小易算算他会涂画多少个棋格。
输入描述:
输入数据包括n+1行:
第一行为一个整数n(1 ≤ n ≤ 50),即棋盘的大小
接下来的n行每行一个字符串表示第i行棋盘的颜色,'W'表示白色,'B'表示黑色
输出描述:
输出小易会涂画的区域大小
示例1
输入
3 BWW BBB BWB
输出
3
题意就是求列里连续同样颜色最多的列,简单题。
import java.util.*;
public class Main {
public static void main(String[] args) {
red();
}
public static void red() {
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
String[] strings = new String[num];
for (int i = 0; i < strings.length; i++) {
strings[i] = scanner.next();
}
char[][] arr = new char[num][num];
for (int i = 0; i < num; i++) {
arr[i] = strings[i].toCharArray();
}
int max = Integer.MIN_VALUE;
// 每一列连续相同颜色最多
for (int i = 0; i < num; i++) {
int temp = 0, start = 0, end = start + 1;
while (start < end && end < num) {
while (end < num && arr[start][i] == arr[end][i]) {
end++;
}
temp = end - start;
max = Math.max(max, temp);
start = end;
end++;
}
}
System.out.println(max);
}
}
[编程题]小易记单词
小易参与了一个记单词的小游戏。游戏开始系统提供了m个不同的单词,小易记忆一段时间之后需要在纸上写出他记住的单词。小易一共写出了n个他能记住的单词,如果小易写出的单词是在系统提供的,将获得这个单词长度的平方的分数。注意小易写出的单词可能重复,但是对于每个正确的单词只能计分一次。
输入描述:
输入数据包括三行:
第一行为两个整数n(1 ≤ n ≤ 50)和m(1 ≤ m ≤ 50)。以空格分隔
第二行为n个字符串,表示小易能记住的单词,以空格分隔,每个单词的长度小于等于50。
第三行为m个字符串,系统提供的单词,以空格分隔,每个单词的长度小于等于50。
输出描述:
输出一个整数表示小易能获得的分数
示例1
输入
3 4 apple orange strawberry strawberry orange grapefruit watermelon
输出
136
简单题,默写的单词与书上的单词对比,相同的加入set去重。
import java.util.*;
public class Main {
public static void main(String[] args) {
jidanci();
}
public static void jidanci() {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int m=scanner.nextInt();
//默写的单词,可能重复
String []moxie=new String[n];
for(int i=0;i<n;i++){
moxie[i]=scanner.next();
}
//给出的单词
String []book=new String[m];
for(int i=0;i<m;i++){
book[i]=scanner.next();
}
//放入set去重
HashSet<String>set=new HashSet<>();
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(book[i].equals(moxie[j])){
set.add(book[i]);
}
}
}
Iterator<String>iterator=set.iterator();
int grade=0;
while(iterator.hasNext()){
int len=iterator.next().length();
grade+=len*len;
}
System.out.println(grade);
}
}
[编程题]堆砖块
小易有n块砖块,每一块砖块有一个高度。小易希望利用这些砖块堆砌两座相同高度的塔。为了让问题简单,砖块堆砌就是简单的高度相加,某一块砖只能使用在一座塔中一次。小易现在让能够堆砌出来的两座塔的高度尽量高,小易能否完成呢。
输入描述:
输入包括两行: 第一行为整数n(1 ≤ n ≤ 50),即一共有n块砖块 第二行为n个整数,表示每一块砖块的高度height[i] (1 ≤ height[i] ≤ 500000)
输出描述:
如果小易能堆砌出两座高度相同的塔,输出最高能拼凑的高度,如果不能则输出-1. 保证答案不大于500000。
示例1
输入
3 2 3 5
输出
5
动态规划,牛客网大佬(https://www.nowcoder.com/questionTerminal/040924ba0e64423b8a3fe2f75a56934a)的答案,没有看懂呢
看懂的留言
链接:https://www.nowcoder.com/questionTerminal/040924ba0e64423b8a3fe2f75a56934a
来源:牛客网
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] h = new int[n];
int sum = 0;
for (int i = 0; i < n; i++) {
h[i] = in.nextInt();
sum += h[i];
}
//本来想用二维数组来存放,但运行会超出内存,所以行不通
int[] dp0 = new int[sum * 2 + 1], dp1 = new int[sum * 2 + 1];
//dp0表示上次放砖时各个位置的情况,dp1表示这次放砖时应该变成的情况
//dp平移sum位是因为B堆-A堆高度的范围在-sum至sum之间,所以平移sum位以便存储每个的值
//所以也就是说j下标为sum时表示A、B堆高度相同时的情况,往左就是B堆较高的情况,往右就是A堆较高的情况
for (int i = 0; i < sum*2 + 1; i++) dp0[i] = -1;
dp0[sum] = 0;
for (int i = 1; i <= n; i++) {
int hg = h[i-1];
//j表示B堆-A堆的高度差(由于平移的原因,sum即为0,往左一次加1,往右依次减1)
//dp1[j]存放的值表示B目前所能存放的最大高度
//dp1[j]是在上次的基础上(也就是dp0)进行变换的
for (int j = 0; j < 2 * sum + 1; j++) {
dp1[j] = dp0[j];
if (j - hg >= 0 && dp0[j - hg] != -1) {
dp1[j] = Math.max(dp0[j - hg], dp1[j]);
}
if (j + hg <= 2 * sum && dp0[j + hg] != -1) {
dp1[j] = Math.max(dp0[j + hg] + hg, dp1[j]);
}
}
//交换两个数组,确保dp0一直是上次的状态
int[] t = dp0;
dp0 = dp1;
dp1 = t;
}
//最后一次变换完为dp0,所以对dp0进行求解
if (dp0[sum] == 0) System.out.println(-1);
else System.out.println(dp0[sum]);
}
}
[编程题]分饼干
易老师购买了一盒饼干,盒子中一共有k块饼干,但是数字k有些数位变得模糊了,看不清楚数字具体是多少了。易老师需要你帮忙把这k块饼干平分给n个小朋友,易老师保证这盒饼干能平分给n个小朋友。现在你需要计算出k有多少种可能的数值
输入描述:
输入包括两行:
第一行为盒子上的数值k,模糊的数位用X表示,长度小于18(可能有多个模糊的数位)
第二行为小朋友的人数n
输出描述:
输出k可能的数值种数,保证至少为1
示例1
输入
9999999999999X 3
输出
4
动态规划,这道题还是有点懵,牛客网一个大佬的代码
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();//模糊数值
int n = sc.nextInt();//小朋友个数
long[][] dp = new long[s.length() + 1][n];//dp[i][j]表示前i个字符串表示的整数除n后余数为j的总数。
dp[0][0] = 1;
for(int i = 1; i <= s.length(); i++) {
for(int j = 0; j < n; j++) {
char c = s.charAt(i - 1);
if(c == 'X') {
//当前字符为X的时候,和下面的分析同理。只是当前位置的数字不是固定的,可能是0-9中的任何一个。
for(int k = 0; k <= 9 ;k++) {
dp[i][(10 * j + k) % n] += dp[i - 1][j];
}
}
else {
//前i-1字符串组成的整数 除n得的余数为j的情况有dp[i - 1][j]种,余数只能在0到n-1的范围
//假设前i-1字符串组成的数是125,n=4,余数为125 % 4 = 1,
//所以dp[3][0]=0,dp[3][1]=1,dp[3][2]=0,dp[3][3] = 0
//假设第i个字符为6,所以新的数变成1256,1256 % n就相当于(125 * 10 + 6) % n
//相当于[(124 + 1)*10 + 6] % n,因为前i-1个组成的数中125 % n = 1,所以(125 - 1) * 10是能除尽n的
//所以就相当于[(1 * 10) + 6] % n ,而 1 就是前i-1个组成的数除n得到的余数,所以前i-1个组成的数除n有多少个余
//数为1的情况,那么前i个组成的数就有多少种余数为[(1*10) + 6] % n 的情况。
//for(int j = 0; j < n; j++) 此for循环中的j代表前i-1个组成的数除n得到的余数,余数从0到n-1
dp[i][(10 * j + c - '0') % n] += dp[i - 1][j];
}
}
}
System.out.println(dp[s.length()][0]);
}
}