闲聊蓝桥杯JAVA - 算式填符号

算式填符号

D:来一道看起来有点麻烦的题目

匪警请拨110,即使手机欠费也可拨通!
为了保障社会秩序,保护人民群众生命财产安全,警察叔叔需要与罪犯斗智斗勇,因而需要经常性地进行体力训练和智力训练!

某批警察叔叔正在进行智力训练:
1 2 3 4 5 6 7 8 9 = 110

请看上边的算式,为了使等式成立,需要在数字间填入加号或者减号(可以不填,但不能填入其它符号)。之间没有填入符号的数字组合成一个数,例如:12+34+56+7-8+9 就是一种合格的填法;123+4+5+67-89 是另一个可能的答案。

请你利用计算机的优势,帮助警察叔叔快速找到所有答案。
每个答案占一行。形如:
12+34+56+7-8+9
123+4+5+67-89
......

Z:题目答案主要有两种形式:

  1. 循环的对象是符号,可能是 + 或 -
  2. 可能没有符号,数字会组合。

M:单单循环符号感觉就是一件很麻烦的事情,怎么做到进行符号的循环呢?

Z:如果要使用+-符号的话,其实不需要循环,直接写 +&- 的两种情况即可。但是呢,总共有9的数字,符号位置有8个,也就是说可能性为2^8次方,也可以找到答案。

但我们这里如果想要压缩八次循环为一行代码,就需要用到递归。递归循环符号的方式就是将两种符号的情况都列出来,然后用一个字符串记录符号的使用情况。

以下为代码实现:

class Main3 {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7,8,9};
        f(arr,8,"",110);
    }
    /**
     * 输出填入符号
     * @param arr 随时变化的数组
     * @param k   当前目标的指针
     * @param result  整条算式的最终结果
     * @param goal   目标值
     */
    private static void f(int[] arr, int k, String result, int goal) {

        if(k == 0){
            if(arr[0] == goal){
                System.out.println(arr[0] + result);
            }
            return;
        }
        //相似性
        f(arr,k-1,"+"+arr[k]+result, goal-arr[k]);
        f(arr,k-1,"-"+arr[k]+result, goal+arr[k]);
        //融合
        int old = arr[k-1];
        arr[k-1] = Integer.parseInt(""+arr[k-1]+arr[k]);
        f(arr,k-1,result,goal);
        arr[k-1] = old;    //回溯
    }

}

M:怎么确定这条递归式子的参数呢?

    /**
     * 输出填入符号
     * @param arr 随时变化的数组
     * @param k   当前目标的指针
     * @param result  整条算式的最终结果
     * @param goal   目标值
     */
private static void f(int[] arr, int k, String result, int goal) {

Z:因为我们这题目答案有两个形式:1.符号变化 2.数字融合

@param arr:要表现数字融合,就需要对数组进行操作,所以要把arr传进来。

@param k:因为有多个符号位置,指针用来确定当前处理哪个位置。

@param result:输出值,结果的显示。

@param goal:验证值,判断得数是否为110,需要有一个目标值进行验证。

M:为什么指针k要反着从8~0?

Z:假设算式为12+34+56+7-8+9,那么它可以分割成12 +34 +56 +7 -8 +96块,其中2-6块都是符号+数字的形式,而第一块需要进行特殊处理。编程应该先考虑普遍情况,再处理特殊值,所以从后面倒着进行循环。

M:为什么下面有一个比较特殊的递归情况,而且只有它需要回溯

        //融合
        int old = arr[k-1];
        arr[k-1] = Integer.parseInt(""+arr[k-1]+arr[k]);
        f(arr,k-1,result,goal);
        arr[k-1] = old;    //回溯

Z:按照规则:符号位可能有三种情况:+ ,- ,空并且与前数融合

当+ 或 - 的情况下,一旦不符合情况,返回上一层仍然与进入该方法前一样,参数int k, String result, int goal都保持不变。

而当融合时,是需要改变arr的值的。在整个递归嵌套过程中,都是使用同一个arr,如果不在执行完之后对arr进行恢复,就会影响后边的程序运算。

简单来说:这是涉及到参数的类型,参数主要有 基本数据类型 和 引用数据类型,当参数为引用数据类型的时候(算法中一般为数组),就需要进行回溯。

M:那出口的设定是怎么想到的呢?

        if(k == 0){
            if(arr[0] == goal){
                System.out.println(arr[0] + result);
            }
            return;
        }

Z:观察式子的变化,其中goal是无所谓为负数的,所以只有@param k 当前目标的指针 会降为0 。当k=0的时候,脚标为0的arr还没有进行运算,所以判断它是否为剩余的goal值,是的话输出组合后的最终结果。

M:总结一下:因为多个符号需要填充,所以使用递归的方式确定符号内容,用一个数组进行记录输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值