LeetCode231 2 的幂 / 第53场双周赛

231. 2 的幂

2021.5.30每日一题

题目描述
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。

如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。


示例 1:

输入:n = 1
输出:true
解释:20 = 1
示例 2:

输入:n = 16
输出:true
解释:24 = 16
示例 3:

输入:n = 3
输出:false
示例 4:

输入:n = 4
输出:true
示例 5:

输入:n = 5
输出:false

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

思路

昨天双周赛后看了一眼,今天每日一题就完事了

class Solution {
    //这道题,来学两个技巧:
    //第一个,x & (x-1) 这个操作使最右边的1变成0
    //第二个,x & (-x)  -x是x取反再加1, 这个操作取出来最右边的1,这两个都可以用在这道题
    public boolean isPowerOfTwo(int n) {
        if (n == 0) return false;
        long x = (long) n;
        return (x & (-x)) == x;
        //return (x &(x - 1)) == 0;
    }
}

第53场双周赛

咋说呢,昨天晚上在实验室做的,本来想着十二点关门,应该能撑过去,结果11点零几就来清人了,我…然后就着急着回去宿舍,打开电脑,结果不知道咋的黑屏了,又搞了十分钟, 一折腾二三十分钟没了。但是昨天做的还是不如意,前两道题还算顺利吧,第三道也就是个模拟,结果测试用例都一下过了,最后提交是错的,又把我弄懵了,到了十二点还没看出来。结果十二点一过,我再看,就知道问题在哪了,最后1200多名,如果第三道过了,估计应该能六七百吧,有点时间说不定还能做出来最后一道哈哈,还是有点遗憾,今天周赛再加油吧

5754. 长度为三且各字符不同的子字符串

题目描述
如果一个字符串不含有任何重复字符,我们称这个字符串为 好 字符串。

给你一个字符串 s ,请你返回 s 中长度为 3 的 好子字符串 的数量。

注意,如果相同的好子字符串出现多次,每一次都应该被记入答案之中。

子字符串 是一个字符串中连续的字符序列。


示例 1:

输入:s = "xyzzaz"
输出:1
解释:总共有 4 个长度为 3 的子字符串:"xyz","yzz","zza" 和 "zaz" 。
唯一的长度为 3 的好子字符串是 "xyz" 。
示例 2:

输入:s = "aababcabc"
输出:4
解释:总共有 7 个长度为 3 的子字符串:"aab","aba","bab","abc","bca","cab" 和 "abc" 。
好子字符串包括 "abc","bca","cab" 和 "abc" 。

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

思路

一看字符串长度觉得暴力能过,直接写了暴力解

class Solution {
    public int countGoodSubstrings(String s) {
        int l = s.length();
        int count = 0;
        for(int i = 2; i < l; i++){
            char a = s.charAt(i - 2);
            char b = s.charAt(i - 1);
            char c = s.charAt(i);
            if(a != b && b != c && a != c){
                count++;
            }
        }
        return count;
    }
}

5755. 数组中最大数对和的最小值

题目描述
一个数对 (a,b) 的 数对和 等于 a + b 。最大数对和 是一个数对数组中最大的 数对和 。

比方说,如果我们有数对 (1,5) ,(2,3) 和 (4,4),最大数对和 为 max(1+5, 2+3, 4+4) = max(6, 5, 8) = 8 。
给你一个长度为 偶数 n 的数组 nums ,请你将 nums 中的元素分成 n / 2 个数对,使得:

nums 中每个元素 恰好 在 一个 数对中,且
最大数对和 的值 最小 。
请你在最优数对划分的方案下,返回最小的 最大数对和 。


示例 1:

输入:nums = [3,5,2,3]
输出:7
解释:数组中的元素可以分为数对 (3,3) 和 (5,2) 。
最大数对和为 max(3+3, 5+2) = max(6, 7) = 7 。
示例 2:

输入:nums = [3,5,4,2,4,6]
输出:8
解释:数组中的元素可以分为数对 (3,5),(4,4) 和 (6,2) 。
最大数对和为 max(3+5, 4+4, 6+2) = max(8, 8, 8) = 8 。

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

思路

应该算是贪心吧,要想使得最大数对和最小,就是最大和最小配对。开始在考虑其他方法,过了一会还是反应过来了

class Solution {
    public int minPairSum(int[] nums) {
        //想多了,排序,然后从两边向中间求和
        int l = nums.length;
        Arrays.sort(nums);
        int left = 0;
        int right = l - 1;
        int max = 0;
        while(left < right){
            max = Math.max(max, (nums[left] + nums[right]));
            left++;
            right--;
        }
        return max;
    }
}

5757. 矩阵中最大的三个菱形和

题目描述
给你一个 m x n 的整数矩阵 grid 。

菱形和 指的是 grid 中一个正菱形 边界 上的元素之和。本题中的菱形必须为正方形旋转45度,且四个角都在一个格子当中。下图是四个可行的菱形,每个菱形和应该包含的格子都用了相应颜色标注在图中。

在这里插入图片描述

注意,菱形可以是一个面积为 0 的区域,如上图中右下角的紫色菱形所示。

请你按照 降序 返回 grid 中三个最大的 互不相同的菱形和 。如果不同的和少于三个,则将它们全部返回。

示例 1:
在这里插入图片描述

输入:grid = [[3,4,5,1,3],[3,3,4,2,3],[20,30,200,40,10],[1,5,5,4,1],[4,3,2,2,5]]
输出:[228,216,211]
解释:最大的三个菱形和如上图所示。
- 蓝色:20 + 3 + 200 + 5 = 228
- 红色:200 + 2 + 10 + 4 = 216
- 绿色:5 + 200 + 4 + 2 = 211

示例 2:
在这里插入图片描述

输入:grid = [[1,2,3],[4,5,6],[7,8,9]]
输出:[20,9,8]
解释:最大的三个菱形和如上图所示。
- 蓝色:4 + 2 + 6 + 8 = 20
- 红色:9 (右下角红色的面积为 0 的菱形)
- 绿色:8 (下方中央面积为 0 的菱形)
示例 3:

输入:grid = [[7,7,7]]
输出:[7]
解释:所有三个可能的菱形和都相同,所以返回 [7] 。

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

思路

看到的第一眼想到的就是模拟,然后用一个数存储三个最大的值,因为是排序并且不重复的,也是这样实现的,可能代码有点复杂,但思路并不难,我当时写的代码是这样的

class Solution {
    int m;
    int n;
    public int[] getBiggestThree(int[][] grid) {
        TreeSet<Integer> set = new TreeSet<>();
        
        m = grid.length;
        n = grid[0].length;
        int l = m < n ? (m + 1) / 2 : (n + 1) / 2;
        //遍历所有的中心点
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                for(int k = 1; k <= l; k++){
                    int cur = 0;
                    if(k == 1)
                        cur = grid[i][j];
                    //左顶点
                    else{
                        int left = j - k + 1;
                        int right = j + k - 1;
                        int up = i - k + 1;
                        int down = i + k - 1;
                        if(!isinner(i, left) || !isinner(i, right) || !isinner(up, j) || !isinner(down, j))
                            continue;
                        cur = count(grid, i, j, left, right, up, down);
                    }
                    if(set.size() < 3)
                        set.add(cur);
                    else{
                        if(cur > set.first()){
                            set.pollFirst();
                            set.add(cur);
                        }
                    }
                    
                }
                
            }
        }
        int[] res = new int[3];
        int i = 0;
        for(i = 0; i < 3; i++){
            if(set.isEmpty())
                break;
            res[i] = set.pollLast();
        }
        
        return Arrays.copyOfRange(res, 0, i);
    }
    
    public boolean isinner(int i, int j){
        return i >= 0 && i < m && j >= 0 && j < n;
    }
    
    public int count(int[][] a, int i, int j, int left, int right, int up, int down){
        //从左边开始i,left > up,j
        int res = 0;
        for(int x = i; x > up; x--){
            for(int y = left; y < j; y++)
                res += a[x][y];
        }
        //up,j>i,right
        for(int x = up; x < i; x++){
            for(int y = j; y < right; y++)
                res += a[x][y];
        }
        //i,right>down,j
        for(int x = i; x < down; x++){
            for(int y = right; y > j; y--)
                res += a[x][y];
        }
        for(int x = down; x > i; x--){
            for(int y = j; y > left; y--)
                res += a[x][y];
        }
        return res;
    }
}

测试用例都秒过,但是一提交就错,搞的我不知道改哪了,后面时间过了以后认真看了一下,发现计算边的时候,两层循环我是嵌套的…当时就给了自己十几拳
改了这个问题以后,再提交,最后一个测试用例没过,然后又看着给的答案想了半天,发现是添加到set集合中的时候,如果当前计算值,大于set中的最小值,但是却和set中其他值相同的话,也会添加到set集合中,导致这个值不会被记录下来,同时又把应该有的值给丢失了,所以需要在加入set集合那里加一句判断set中是否有当前元素这句话
唉,继续加油吧,做题的时候还是不能着急

class Solution {
    int m;
    int n;
    public int[] getBiggestThree(int[][] grid) {
        TreeSet<Integer> set = new TreeSet<>();
        
        m = grid.length;
        n = grid[0].length;
        int l = m < n ? (m + 1) / 2 : (n + 1) / 2;
        //遍历所有的中心点
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                for(int k = 1; k <= l; k++){
                    int cur = 0;
                    if(k == 1)
                        cur = grid[i][j];
                    //左顶点
                    else{
                        int left = j - k + 1;
                        int right = j + k - 1;
                        int up = i - k + 1;
                        int down = i + k - 1;
                        if(!isinner(i, left) || !isinner(i, right) || !isinner(up, j) || !isinner(down, j))
                            continue;
                        cur = count(grid, i, j, left, right, up, down);
                    }
                    if(set.size() < 3)
                        set.add(cur);
                    else{
                        if(!set.contains(cur) && cur > set.first()){
                            set.pollFirst();
                            set.add(cur);
                        }
                    }
                    
                }
                
            }
        }
        int[] res = new int[set.size()];
        for(int i = 0; i < res.length; i++){
            res[i] = set.pollLast();
        }
        
        return res;
    }
    
    public boolean isinner(int i, int j){
        return i >= 0 && i < m && j >= 0 && j < n;
    }
    
    public int count(int[][] a, int i, int j, int left, int right, int up, int down){
        //从左边开始i,left > up,j
        int res = 0;
        for(int x = i, y = left; x > up; x--, y++){
            //for(int y = left; y < j; y++)
            res += a[x][y];
        }
        //up,j>i,right
        for(int x = up, y = j; x < i; x++, y++){
            //for(int y = j; y < right; y++)
            res += a[x][y];
        }
        //i,right>down,j
        for(int x = i, y = right; x < down; x++, y--){
            //for(int y = right; y > j; y--)
            res += a[x][y];
        }
        for(int x = down, y = j; x > i; x--, y--){
            //for(int y = j; y > left; y--)
            res += a[x][y];
        }
        return res;
    }
}

5756. 两个数组最小的异或值之和

题目描述
给你两个整数数组 nums1 和 nums2 ,它们长度都为 n 。

两个数组的 异或值之和 为 (nums1[0] XOR nums2[0]) + (nums1[1] XOR nums2[1]) + ... + (nums1[n - 1] XOR nums2[n - 1]) (下标从 0 开始)。

比方说,[1,2,3] 和 [3,2,1] 的 异或值之和 等于 (1 XOR 3) + (2 XOR 2) + (3 XOR 1) = 2 + 0 + 2 = 4 。
请你将 nums2 中的元素重新排列,使得 异或值之和 最小 。

请你返回重新排列之后的 异或值之和 。


示例 1:

输入:nums1 = [1,2], nums2 = [2,3]
输出:2
解释:将 nums2 重新排列得到 [3,2] 。
异或值之和为 (1 XOR 3) + (2 XOR 2) = 2 + 0 = 2 。
示例 2:

输入:nums1 = [1,0,3], nums2 = [5,3,4]
输出:8
解释:将 nums2 重新排列得到 [5,4,3] 。
异或值之和为 (1 XOR 5) + (0 XOR 4) + (3 XOR 3) = 4 + 4 + 0 = 8 。

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

思路

这道题就在回去的路上简单想了想,觉得应该是位运算
但是今天看到数组长度最多14,突然事情没这么简单,感觉可以枚举
去看题解了,状态压缩dp,一时半会真看不懂,更别说自己想到了

31号又看了半天,终于有点眉目了。
首先看到数组长度最大为14,想到可以状压;然后把nums2中各位位置元素被选择的状态,定义成一个长度为n的二进制数mask。mask从低到高的第 i 位为1,说明nums2中国第 i 个位置的数被选择了
定义f[mask]为在nums2中选择元素状态为mask的情况下,并且选择了nums1中前count(mask)【表示mask中1的个数】个元素的情况下,组成的最小异或和
那么对于每一个状态,枚举此时选择的nums1中的元素也就是nums1[count(mask)],与nums2中哪一个元素进行了异或运算,并且在找到前面的状态,也就是让mask中(在nums2中选择元素的位置)为0,加起来就是当前的状态值,具体看代码

考虑完这个状态转移以后,再看状态定义,就会更加通透,状态定义是选择了nums1中前c个,nums2中是按mask选的,而状态转移呢,这2c个元素共有c的平方个组合,而状态转移让这种完全列举的情况,变成了c种,也就是从之前c-1的状态转移而来。即考虑两个数组中的第c个元素(从1开始),对于nums1中就是第c个,对于nums2中就是mask中1的位置;而前面c-1的状态,已经完成了,因为mask是从小到大增大的。

class Solution {
    public int minimumXORSum(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int[] dp = new int[(1 << n)];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        //遍历所有状态
        for(int mask = 1; mask < (1 << n); mask++){
            int c = Integer.bitCount(mask);
            //遍历n个数
            for(int i = 0; i < n; i++){
                //如果第i个数在nums2中被选择
                if((mask & (1 << i)) > 0){
                    //mask ^ (1 << i),让mask的第i位变成0
                    dp[mask] = Math.min(dp[mask], dp[mask ^ (1 << i)] + (nums1[c - 1] ^ nums2[i]));
                }
            }
        }
        return dp[(1 << n) - 1];
    }
}

这种题还要多练练

小结

这次比赛比上次有进步,尽管排名没有进步啊哈哈;但是三道题应该还是能做出来的,目标还是三道题,遇到代码多的要更加考虑好细节,加油!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答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、付费专栏及课程。

余额充值