参考来源:https://blog.csdn.net/weixin_40163242/article/details/87932218
【问题描述】
易老师购买了一盒饼干,盒子中一共有k块饼干,但是数字k有些数位变得模糊了,看不清楚数字具体是多少了。易老师需要你帮忙把这k块饼干平分给n个小朋友,易老师保证这盒饼干能平分给n个小朋友。现在你需要计算出k有多少种可能的数值
输入描述:
输入包括两行:
第一行为盒子上的数值k,模糊的数位用X表示,长度小于18(可能有多个模糊的数位)
第二行为小朋友的人数n
输出描述:
输出k可能的数值种数,保证至少为1
输入例子:
9999999999999X
3
输出例子:
4
这道题的状态着实不好想,最后看网上的思路也是反复看了好几遍才看懂。原来它将dp[i][j]设为了字符串前i位表示的数%n = j的情况数。换句话说,就是用j来枚举余数。
至于为什么要这样设呢?
举个例子,我们很容易求出13/3的余数为1,那么134/3的余数呢?其实它的余数是14%3=2,容易发现,14就是高位的13除以3而得到的余数1与低位的4组合出来的。134/3,写一写小学除法的竖式,你会看到13余下来的1与个位的4组成了一个新数14。
发现了这个规律就已经解决了一大半了,状态方程是这样的:dp[i][newj] += dp[i - 1][j] 。
其中newj解释为:由以上的规律我们得出了结论,如果设上一位的余数是j,设这一位的数为x, 那么我们这一位的余数一定是(10 * j + x) % n对吧。因此,我们将本位的余数保存在newj变量中,即newj = (10 * j + x) % n。所以,如果前一位的余数j对应的情况数已知,那么本位的余数newj对应的情况数不也就已知了吗?所以有dp[i][newj] += dp[i - 1][j]。
还有为什么要+=呢?这个问题我也想了很长时间。因为dp数组存的是情况数,那么前i位的情况里面肯定包含前i-1位的情况,所以位数长的情况数自然要加上位数短的情况数。
下面给出代码:
import java.util.*;
public class Main {
static String s;
static int n;
static int[][] dp;
static int len;
static int DP(){
//边界,第一位
if(s.charAt(0)=='X'){
for(int i=1;i<=9;i++){//枚举数值,第一位数不可能是0
for(int j=0;j<n;j++){//枚举余数
if(i%n==j){
dp[1][j]++;
}
}
}
}
else{
int x=s.charAt(0)-'0';//首位数字
int j=x%n;//首位数字的余数
dp[1][j]++;
}
//后面的位
for(int i=2;i<=len;i++){
for(int j=0;j<n;j++){
if(s.charAt(i-1)=='X'){
for(int k=0;k<=9;k++){
int newj=(10*j+k)%n;//前i位的余数
dp[i][newj]+=dp[i-1][j];//前i位的情况里面包含前i-1位的情况
}
}
else{
int newj=(10*j+(s.charAt(i-1)-'0'))%n;
dp[i][newj]+=dp[i-1][j];//前i位的情况里面包含前i-1位的情况
}
}
}
//打印dp数组,结果写在纸质报告里面
for(int i=1;i<=len;i++){
for(int j=0;j<n;j++){
System.out.print(dp[i][j]+" ");
}
System.out.println();
}
return dp[len][0];
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
s=in.next();
len=s.length();
n=in.nextInt();
dp=new int[len+1][n];
System.out.println(DP());
}
}