LeetCode 504. 七进制数 / 2055. 蜡烛之间的盘子 / 798. 得分最高的最小轮调(差分数组)

504. 七进制数

2022.3.7 每日一题

题目描述

给定一个整数 num,将其转化为 7 进制,并以字符串形式输出。

示例 1:

输入: num = 100
输出: “202”

示例 2:

输入: num = -7
输出: “-10”

提示:

-10^7 <= num <= 10^7

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/base-7
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution {
    public String convertToBase7(int num) {
        if(num == 0)
            return "0";
        boolean flag = false;   //标记正负数
        if(num >= 0)
            flag = true;
        //转成正数
        if(!flag) num = -num;

        StringBuilder sb = new StringBuilder();

        while(num != 0){
            sb.append(String.valueOf(num % 7));
            num = num / 7;
        }

        if(!flag) 
            sb.append("-");
        sb.reverse();

        return sb.toString();

    }
}

2055. 蜡烛之间的盘子

2022.3.8 每日一题,祝所有女性朋友节日快乐,永远年轻快乐

题目描述

给你一个长桌子,桌子上盘子和蜡烛排成一列。给你一个下标从 0 开始的字符串 s ,它只包含字符 ‘’ 和 ‘|’ ,其中 '’ 表示一个 盘子 ,’|’ 表示一支 蜡烛 。

同时给你一个下标从 0 开始的二维整数数组 queries ,其中 queries[i] = [lefti, righti] 表示 子字符串 s[lefti…righti] (包含左右端点的字符)。对于每个查询,你需要找到 子字符串中 在 两支蜡烛之间 的盘子的 数目 。如果一个盘子在 子字符串中 左边和右边 都 至少有一支蜡烛,那么这个盘子满足在 两支蜡烛之间 。

比方说,s = “|||||" ,查询 [3, 8] ,表示的是子字符串 "||**|” 。子字符串中在两支蜡烛之间的盘子数目为 2 ,子字符串中右边两个盘子在它们左边和右边 都 至少有一支蜡烛。
请你返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。

示例 1:

在这里插入图片描述
输入:s = “||***|”, queries = [[2,5],[5,9]]
输出:[2,3]
解释:
-queries[0] 有两个盘子在蜡烛之间。
-queries[1] 有三个盘子在蜡烛之间。

示例 2:

在这里插入图片描述
输入:s = “||||||*”, queries = [[1,17],[4,5],[14,17],[5,11],[15,16]]
输出:[9,0,0,0,0]
解释:
-queries[0] 有 9 个盘子在蜡烛之间。
-另一个查询没有盘子在蜡烛之间。

提示:

3 <= s.length <= 10^5
s 只包含字符 ‘*’ 和 ‘|’ 。
1 <= queries.length <= 10^5
queries[i].length == 2
0 <= lefti <= righti < s.length

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/plates-between-candles
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

二分+前缀和
我这里直接用的TreeMap

class Solution {
    public int[] platesBetweenCandles(String s, int[][] queries) {
        //我能想到的思路是用一个treemap,键是每个蜡烛的位置,值是右边到下一个蜡烛有多个盘子
        //然后查找的时候,先找到左边下标最近(右边)的蜡烛,然后找到右边下标最近(左边)的蜡烛
        //然后遍历treemap,把这个范围内的盘子都加起来
        //这样的话,因为是离线查询,极限是On方,应该会超时,不过先写一下吧
        //不不,应该统计蜡烛左边有多少盘子,这样就和前缀和一样了

        int l = s.length();
        TreeMap<Integer, Integer> map = new TreeMap<>();
        int idx = -1;
        int count = 0;

        //统计每个蜡烛右边有多少个盘子
        for(int i = 0; i < l; i++){
            char c = s.charAt(i);
            if(c == '|'){
                idx = i;
                map.put(idx, count);
            }else{
                count++;
            }
        }

        //查询
        int n = queries.length;
        int[] res = new int[n];
        for(int i = 0; i < n; i++){
            int[] temp = queries[i];
            if(map.ceilingKey(temp[0]) == null){
                res[i] = 0;
                continue;
            }
            int left = map.ceilingKey(temp[0]);
            if(map.floorKey(temp[1]) == null){
                res[i] = 0;
                continue;
            }
            int right = map.floorKey(temp[1]);
            int ll = map.get(left);
            int rr = map.get(right);
            res[i] = rr - ll;
            if(left >= right)
                res[i] = 0;
        }
        return res;
    }
}

798. 得分最高的最小轮调

2022.3.9 每日一题

题目描述

给你一个数组 nums,我们可以将它按一个非负整数 k 进行轮调,这样可以使数组变为 [nums[k], nums[k + 1], … nums[nums.length - 1], nums[0], nums[1], …, nums[k-1]] 的形式。此后,任何值小于或等于其索引的项都可以记作一分。

例如,数组为 nums = [2,4,1,3,0],我们按 k = 2 进行轮调后,它将变成 [1,3,0,2,4]。这将记为 3 分,因为 1 > 0 [不计分]、3 > 1 [不计分]、0 <= 2 [计 1 分]、2 <= 3 [计 1 分],4 <= 4 [计 1 分]。
在所有可能的轮调中,返回我们所能得到的最高分数对应的轮调下标 k 。如果有多个答案,返回满足条件的最小的下标 k 。

示例 1:

输入:nums = [2,3,1,4,0]
输出:3
解释:
下面列出了每个 k 的得分:
k = 0, nums = [2,3,1,4,0], score 2
k = 1, nums = [3,1,4,0,2], score 3
k = 2, nums = [1,4,0,2,3], score 3
k = 3, nums = [4,0,2,3,1], score 4
k = 4, nums = [0,2,3,1,4], score 3
所以我们应当选择 k = 3,得分最高。

示例 2:

输入:nums = [1,3,0,2,4]
输出:0
解释:
nums 无论怎么变化总是有 3 分。
所以我们将选择最小的 k,即 0。

提示:

1 <= nums.length <= 10^5
0 <= nums[i] < nums.length

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-rotation-with-highest-score
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

能直接想到的就是超时的方法,统计每个位置可以移动的次数,然后找到使分数最高的次数

class Solution {
    public int bestRotation(int[] nums) {
        //就是旋转数组,然后看哪个数组分最高
        //统计每一个数,在哪几个位置是满足条件的,然后将需要移动多少次,放在一个set集合中
        //因为最多移动长度减1次,所以统计哪一次分最高,然后输出这个最高分

        //例如第一个例子,2可以向左移动1,2,3次,3移动2,3次,1需要向左移动1 3 4次,4需要4次,0需要0 1 2 3 4次
        //那么可以看到,移动3次是最优选择,因为有4分
    
        int l = nums.length;
        
        Map<Integer, List<int[]>> map = new HashMap<>();
        for(int i = 0; i < l; i++){
            int t = nums[i];
            List<int[]> list = new ArrayList<>();
            //如果t大于下标i,那么需要移动到t到length - 1的位置,也就是说,需要移动i+1到i+1+length-1-t次
            if(t > i){
                list.add(new int[]{(i + 1) % l, (i + l - t) % l});
            //如果小于等于的时候,分两段
            //例如示例1中的1,那么比它大的位置肯定是满足条件的,就和上面情况相同,比它小的位置,也就是t到i,也是满足条件的
            }else{
                if(i + 1 <= l - 1)
                    list.add(new int[]{(i + 1) % l, l - 1});
                list.add(new int[]{0, i - t});
            }
            /*
            System.out.println(list.get(0)[0] + "+++");
            System.out.println(list.get(0)[1]);
            if(list.size() > 1){
                System.out.println(list.get(1)[0] + "---");
                System.out.println(list.get(1)[1] + "--------");
            }*/
            map.put(i, list);
        }

        int max = 0;
        int count = 0;
        //当移动这么多次时,几个满足条件
        for(int i = 0; i < l; i++){
            int m = 0;
            for(int t : map.keySet()){
                List<int[]> list = map.get(t);
                int[] t1 = list.get(0);
                if(i >= t1[0] && i <= t1[1])
                    m++;
                else if(list.size() > 1){
                    int[] t2 = list.get(1);
                    if(i >= t2[0] && i <= t2[1])
                        m++;
                }
            }
            if(m > max){
                max = m;
                count = i;
            }
        }
        return count;

    }
}

那么怎么做改进呢
我们通过一次遍历,得到了每个位置可以移动的次数范围
知道这个范围以后,可以通过差分数组来实现对次数的统计
具体来说,对于一个数x,如果它移动的范围(即可以得分)是[left,right]
那么利用差分数组的思想,先创建一个差分数组diff[],然后令diff[left] + 1,令diff[right + 1] - 1
意思是到left这个位置,分数开始加1,到right+1这个位置,分数开始-1
然后计算这个差分数组的前缀和,就可以得到向左移动几次,得到的分数最高了

class Solution {
    public int bestRotation(int[] nums) {
        //就是旋转数组,然后看哪个数组分最高
        //统计每一个数,在哪几个位置是满足条件的,然后将需要移动多少次,放在一个set集合中
        //因为最多移动长度减1次,所以统计哪一次分最高,然后输出这个最高分

        //例如第一个例子,2可以向左移动1,2,3次,3移动2,3次,1需要向左移动1 3 4次,4需要4次,0需要0 1 2 3 4次
        //那么可以看到,移动3次是最优选择,因为有4分
    
        int l = nums.length;
        
        Map<Integer, List<int[]>> map = new HashMap<>();
        for(int i = 0; i < l; i++){
            int t = nums[i];
            List<int[]> list = new ArrayList<>();
            //如果t大于下标i,那么需要移动到t到length - 1的位置,也就是说,需要移动i+1到i+1+length-1-t次
            if(t > i){
                list.add(new int[]{(i + 1) % l, (i + l - t) % l});
            //如果小于等于的时候,分两段
            //例如示例1中的1,那么比它大的位置肯定是满足条件的,就和上面情况相同,比它小的位置,也就是t到i,也是满足条件的
            }else{
                if(i + 1 <= l - 1)
                    list.add(new int[]{(i + 1) % l, l - 1});
                list.add(new int[]{0, i - t});
            }
            /*
            System.out.println(list.get(0)[0] + "+++");
            System.out.println(list.get(0)[1]);
            if(list.size() > 1){
                System.out.println(list.get(1)[0] + "---");
                System.out.println(list.get(1)[1] + "--------");
            }*/
            map.put(i, list);
        }

        int[] diff = new int[l + 1];
        for(int i = 0; i < l; i++){
            List<int[]> list = map.get(i);
            int[] t1 = list.get(0);
            diff[t1[0]]++;
            diff[t1[1] + 1]--;
            if(list.size() > 1){
                int[] t2 = list.get(1);
                diff[t2[0]]++;
                diff[t2[1] + 1]--;
            }
        }

        int max = 0;
        int count = 0;
        int score = 0;
        //当移动这么多次时,几个满足条件
        for(int i = 0; i < l; i++){
            score += diff[i];
            if(score > max){
                max = score;
                count = i;
            }
        }
        return count;

    }
}

官解将分成两段的情况也合并成一段了,其实不太好理解

### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的学理论和强大的实用效果,动态规划在计算机科学、学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值