求解分饼干问题(DP)

参考来源: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());
    }
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值