算法=>魔法硬币

作者: 小遁哥 | 来源:发表于2018-07-15 12:44 被阅读2次

    魔法师小遁手里没有硬币,现有A机器,投入硬币后可得到2x+1个,有B机器,投入硬币可得到2x+2 个,
    请帮助小遁获得数目恰好的硬币

    1.穷举法

    在违法的边缘试探.jpg
    let x = 0;
    //已有硬币总数
    let count = 10;
    //想要获得硬币的总数    
    
    let j = 0;
    put_x(0, 0, 0, [])
    /* 
        i 投入的硬币数目
        x 已有硬币数
        size 已有硬币数
        stack  记录投入的信息
    */
    function put_x(i, x, size, stack) {
        if (j++ > 1600) {
            //预防编写阶段进入死循环
            return;
        }
        let current = size;
        let length = stack.length;
        do {
            //先投入A机器
            size = current + 2 * i + 1;
            stack[length] = i;
            stack[length + 1] = 1;
            stack[length + 2] = size;
            if (size < count) {
                put_x(0, size, size, [].concat(stack))
            } else {
                if (size == count) {
                    console.log(size, stack)
                }
                return;
            }
    
            //放入B机器
            //这里重新计算了 size 以及stack的信息
    
            size = current + 2 * i + 2;
            stack[length] = i;
            stack[length + 1] = 2;
            stack[length + 2] = size;
            if (size < count) {
                put_x(0, size, size, [].concat(stack))
            } else {
                if (size == count) {
                    console.log(size, stack)
                }
                return;
    
            }
            i++;
    
        } while (i <= x)
    }
    

    经过上面的思考我发现

    应该总是先投B机器,而且每次尽量投最多的硬币
    每次应该减去投进去的硬币才是最终获得硬币数量

    2 最少的投掷次数

    我觉得ok.jpg
    let j = 0;
    coin(9);
    function coin(count){
    
        //记录投入的信息
        let stack = [];
        
        computer(0,0);
        console.table(stack)
        /*
            size  想要得到的硬币数
        */
        function computer(size,number){
            //防止陷入死循环
            if(j++ > 1600) return;
    
            //由 count = 2*time - 2 - size 推导  
            //得到最多可以投入的硬币数
            //先投入B机器
            let time = Math.floor(count - size - 2);
            
            
            if(time < 0){//再次投入B机器会超出
                //投入A机器
                time =  Math.floor(count - size - 1 );
                number = time + 1 + size ;
                //投入的机器  投入的硬币数  得到的硬币数 上次拥有的硬币数  现拥有的硬币数 
                stack.push(['A',time,2*time+1,size,number])
            }
            else{
                if(time > number){//防止投入的硬币超出已有的
                    time = number;
                }
                number = time + 2 + size;
                stack.push(['B',time,2*time+2,size,number])
            }
            
            if(number < count){
                computer(number,number);
            }
    
        }
    
    }
    
    image.png

    为什么投入A机器不需要time<0的判定,是因为time数是推算出来的,如果B机器不能投,A机器也不能投,那岂不是存在无法得到的数?

    是否真的存在当前算法无法得到的数?

    经过几个数的测试 我发现如下规律

    需要一个1的时候才会用到A 因为奇数 = 偶数 - 1
    总是可以通过投入B机器 来减少投入A机器需要得到的硬币数 因为我们先投入B机器
    因为投入B机器的硬币数可以是奇数 => 产生偶数 + 上次总的是偶数 - 投入奇数取 = 奇数
    所以并非所有奇数都会用到A机器(1,3,7会用到)

    综上所述 可以用

    number = count ;
    stack.push(['A',0,1,size,count])
    

    代替if(time < 0) 里面的代码

    然而有一种叫做逆向推理的过程

    为么不计算想要得到总数之前需要投入多少硬币呢,也就是每次把所拥有的硬币都投入进去

    打脸.jpg
    computer(110);
    function computer(count){
    
        let stack = [];
        let size = count;
        let lastSize = size;
        while(size>0){
            
            if(size%2 == 0){
                size = Math.floor((size - 2)/2);
                stack.push(['B',size,lastSize])
            }
            else{
                size = Math.floor((size - 1)/2)
                stack.push(['A',size,lastSize])
            }
            lastSize = size;
            
        }
        console.table(stack)
    }
    

    显然这种解法要优于之前的,之前算法出现不能将已拥有所有硬币放入B机器时,可能会多出一步

    然而之前的推导并非没有意义,是熟悉规律的一个过程。上述程序也是有价值的。

    Tips:
    尝试用console.dir 输出DOM元素
    在script标签上ctrl+shift+/ 便可以 注释整个script标签

    如果我只想得到步骤最少的4个解该怎么办呢!

    相关文章

      网友评论

        本文标题:算法=>魔法硬币

        本文链接:https://www.haomeiwen.com/subject/ctzupftx.html