codecamp

贪心算法 坏了的计算器

题目

难度:简单

在显示着数字的坏计算器上,我们可以执行以下两种操作:

双倍(Double):将显示屏上的数字乘 2; 递减(Decrement):将显示屏上的数字减 1 。

最初,计算器显示数字 X。

返回显示数字 Y 所需的最小操作数。

示例 1:

输入:X = 2, Y = 3
输出:2
解释:先进行双倍运算,然后再进行递减运算 {2 -> 4 -> 3}.

示例 2:

输入:X = 5, Y = 8
输出:2
解释:先递减,再双倍 {5 -> 4 -> 8}.

示例 3:

输入:X = 3, Y = 10
输出:3
解释:先双倍,然后递减,再双倍 {3 -> 6 -> 5 -> 10}.

示例 4:

输入:X = 1024, Y = 1
输出:1023
解释:执行递减运算 1023 次

提示:

1 <= X <= 10^9 1 <= Y <= 10^9

题解一

为什么这道题采用逆向思维更优?

1.刚开始的时候觉得很简单,用了两个while,结果报错了。因为可以先减去再去乘以就会节省次数

正向思维:在X/Y时要实现操作数最小,要将X逼近Y的1/2值或1/4值或1/8值或...再进行*2操作,难点在于要判断要逼近的是1/2值还是1/4值还是其他值,逻辑复杂 逆向思维:在Y>X时Y只管/2,到了Y\<X时在+1逼近 说白了就是,正向思维采用的是先小跨度的-1操作,再大跨度的*2操作;逆向思维采用的是先大跨度的/2操作,再小跨度的-1操作 然而事实上往往是先大后小的解决问题思维在实现起来会比较简单 cur=y;存储yn=0;次数

2.什么时候可以先减去再去乘以?Y是一个偶数 3.所以先把cur+1成为偶数 4.再/2处理,小于X之后跳出循环 5.这时候n+x-cur即为结果x-cur为x与cur之间还需要进行的几次操作

class Solution {
    public int brokenCalc(int X, int Y) {
        int cur=Y,n=0;
        while(X<cur){
            n++;
            if(cur%2==1){
                cur++;
            }
            else{
                cur/=2;
            }
        }




        return n+X-cur;
    }
}

解法二:递归

class Solution {
    public int brokenCalc(int X, int Y) {
        if (X >= Y) {
            return X - Y;
        }


        if (Y % 2 == 1) {
            return 2 + brokenCalc(X, (Y + 1) / 2);
        } else {
            return 1 + brokenCalc(X, Y / 2);
        }
    }
}

解法三:分步

只需要讨论 Y > X时的情况。分为两步统计, cnt1为多少个乘法,cnt2为多少个减法。 显然我们必须把 X 乘到恰好比 Y 大的数,否则再怎么减也达不到要求……因此先求 cnt1. 那么,关键是 cnt2 怎么求呢?我们假设减法穿插在各个乘法之间,如果在第一次乘法前减,那么最终等价于减去 2cnt12^{cnt1}2cnt1, 如果在第二次乘法前减,最终等价于减去 2cnt1−12^{cnt1 - 1}2cnt1−1,以此类推。由于每次可以减多个1,因此最终要乘个系数,减了 a 2cnt12^{cnt1}2cnt1 + b 2cnt1−12^{cnt1 - 1}2cnt1−1 + .... 那么这个系数 a,b,c等等是多少呢,贪心即可,a越大越好,其次到b, c...

class Solution {
    public int brokenCalc(int X, int Y) {
        if (Y <= X) return X - Y;
        int cnt1 = 0;
        while (X < Y) {
            X *= 2;
            cnt1 ++;
        }
        if (X == Y) return cnt1;
        int r = X - Y;
        int cnt2 = 0;
        for (int i = cnt1; i >= 0; i --) {
            int t = (int)Math.pow(2, i);
            int coeff = r / t;
            r = r % t;
            cnt2 += coeff;
            if (r == 0) break;
        }
        return cnt1 + cnt2;
    }
}

贪心算法 玩筹码
滑动窗口的最大值
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

趣味题

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }