还原竖式:下图的乘法竖式中,每一个星号代表一个数位。若出现的数字有且仅有2, 3, 5, 7四种,你能将此竖式完全还原吗?

题目

        下图的乘法竖式中,每一个星号代表一个数位。若出现的数字有且仅有2, 3, 5, 7四种,你能将此竖式完全还原吗?

        进一步,若将题目中的2, 3, 5, 7改为其它互异的四个数字,还存在要求的乘法竖式吗?

        进一步,若将题目中的2, 3, 5, 7改为其它互异的多个(2~10)数字,还存在要求的乘法竖式吗?

算法描述    

        针对基本问题,需要采用深度优先算法,在2, 3, 5, 7四个数字中选取所有的乘数对,并对其进行判断是否满足条件,如果满足条件,则进行打印。

        针对第二个问题,需再套一层回溯算法,在0~9内选出所有4个不同的数的组合,再调用第一个问题中的函数进行寻找符合条件的乘数对。

        在程序中,运算的数字存储在数组中,如365存储在a[3]中,a[0]=5,a[1]=6,a[2]=3,用代码表示为int[] a=new int[]{5,6,3}。

        在对三位数乘两位数分派数字时,先在int[] merge=new int[5]中分配,然后取前三位拆分为第一个乘数,取后二位拆分为第二个乘数。

        分配数字的来源是int[] stock中的,stock的默认值为{2,3,5,7}。

        方法void show(int[] a)表示将数组a以乘数的形式打印出来,如int[] a=new int[]{5,6,3},show(a)的结果为365。

        boolean isProper(int x)用于检测某个数值是否在要求的stock中出现。

        boolean isProper(int[] a, int[] b)用于检查a和b相乘,是否满足条件,即运算中出现的数字是否在stock中。

        void fun(int k)用于将stock中的数字分配到乘数对中,并在所有可能性中找到符合条件的乘数对并将其打印出来。

        void fun2(int j, int k)用于在0~9中选择四个不同的数,存入int[] stock中,并对所用stock的可能性调用fun函数,寻找符合条件的乘数对。

代码实现

import java.util.Arrays;

public class Problem2 {
    //当数字abcd存储在a[4]中时,a0~3分别是d,c,b,a;
    private static int[] number = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};//存储0~9,用于问题二的探讨四个其他互异数字
    private static int[] stock = new int[]{2, 3, 5, 7};//构成的要求数字的基本元素
    private static int[] merge = new int[5];//用于存储两个因数合并,前三位为a后2位为b

    private static void show(int[] a) {
        for (int i = a.length - 1; i >= 0; i--) {
            if (a[i] < 0 || a[i] > 9) {
                throw new IllegalArgumentException("数组的数字应该在0~9之间");
            }//进行参数的合法性校验
            System.out.print(a[i]);
        }
        System.out.println("");
    }//将存储在数组里的数以数值的形式打印出来

    private static boolean isProper(int x) {//单个数值是否符合条件
        if (x >= 10 || x < 0) {
            throw new IllegalArgumentException("请传入十以内的自然数");
        } else return x == stock[0] || x == stock[1] || x == stock[2] || x == stock[3];
    }

    private static boolean isProper(int[] a, int[] b) {//计算指定a2a1a0×b1b0,判断是否符合条件
        if (a.length != 3 || b.length != 2) {//检查参数的合法性
            throw new IllegalArgumentException("数组a应为3位,数组b应为2位");
        }
        for (int x : a) {
            if (!isProper(x)) {
                return false;
            }
        }
        for (int x : b) {
            if (!isProper(x)) {
                return false;
            }
        }
        int[] m = new int[4];
        int[] n = new int[4];//用于存储计算过程中的第一行和第二行
        int[] result = new int[5];//用于存储结果
        int k = 0;//用于存储计算过程中的进位
        boolean[] check = new boolean[10];//check[2],check[3],check[5],ckeck[7]分别用于记录2357是否出现过

        for (int i = 0; i <= 2; i++) {//进行第一行m[4]的计算
            m[i] = (b[0] * a[i] + k) % 10;
            if (!isProper(m[i])) {
                return false;
            } else {
                check[m[i]] = true;
            }//过程中发现不符合条件则返回false
            k = (b[0] * a[i] + k) / 10;
        }
        m[3] = k;
        if (!isProper(m[3])) {
            return false;
        } else {
            check[m[3]] = true;
        }//过程中发现不符合条件则返回false
        k = 0;

        for (int i = 0; i <= 2; i++) {//进行第二行n[4]的计算
            n[i] = (b[1] * a[i] + k) % 10;
            if (!isProper(n[i])) {
                return false;
            } else {
                check[n[i]] = true;
            }//过程中发现不符合条件则返回false
            k = (b[1] * a[i] + k) / 10;
        }
        n[3] = k;
        if (!isProper(n[3])) {
            return false;
        } else {
            check[n[3]] = true;
        }//过程中发现不符合条件则返回false
        k = 0;

        result[0] = m[0];//对result[5]进行计算
        for (int i = 1; i <= 3; i++) {
            result[i] = (m[i] + n[i - 1] + k) % 10;
            if (!isProper(result[i])) {
                return false;
            } else {
                check[result[i]] = true;
            }//过程中发现不符合条件则返回false
            k = (m[i] + n[i - 1] + k) / 10;
        }
        result[4] = k + n[3];
        if (!isProper(result[4])) {
            return false;
        } else {
            check[result[4]] = true;
        }//过程中发现不符合条件则返回false

        return check[stock[0]] && check[stock[1]] && check[stock[2]] && check[stock[3]];
    }

    private static void fun(int k) {//采用深度优先算法,将stock中的数字分配到两个乘数中,并进行判断;k表示merge[k]及之后的未分配到数字
        if (k == 5) {//当merge中全部得到分配后,拆分merge并进行判断
            if (isProper(Arrays.copyOfRange(merge, 0, 3), Arrays.copyOfRange(merge, 3, 5))) {
                show(Arrays.copyOfRange(merge, 0, 3));
                show(Arrays.copyOfRange(merge, 3, 5));
                System.out.println("");
            }//如果拆分后的因数符合条件,打印该因式
            return;
        }
        for (int x : stock) {//merge第k位待分配时,依次分给merge第k位stock的所有值,并进行递归分配第k+1位
            merge[k] = x;
            fun(k + 1);
        }
    }

    private static void fun2(int j, int k) {
        //j表示number中的number[j]及以后没有决定是否选择,k表示已经在number中选择了k个不同的数字放到了stock中
        if (k == 4) {//当已经选择足够的4个不重复数字放入stock中,执行fun函数
            fun(0);
        } else if (j == 10) {//当没有数字可选,且没有选够4个数字时,返回空
            return;
        } else {//有数字可选,且未选够数字时,对number[j]进行选取,并回溯该步骤,对j+1及之后的数字进行选取
            stock[k] = number[j];
            fun2(j + 1, k + 1);
            fun2(j + 1, k);
        }
    }

    public static void main(String[] args) {
        fun(0);//调用此函数,寻找2357的乘数
//        fun2(0, 0);//调用此函数,寻找包括0的四个不相同数字满足要求的乘数
//        fun2(1, 0);//调用此函数,寻找不包括0的四个不相同数字满足要求的乘数
    }
}

运行结果

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值